<!DOCTYPE html>
<html lang=zh>
<head>
  <meta charset="utf-8">
  
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
  <meta name="renderer" content="webkit">
  <meta http-equiv="Cache-Control" content="no-transform" />
  <meta http-equiv="Cache-Control" content="no-siteapp" />
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="format-detection" content="telephone=no,email=no,adress=no">
  <!-- Color theme for statusbar -->
  <meta name="theme-color" content="#000000" />
  <!-- 强制页面在当前窗口以独立页面显示,防止别人在框架里调用页面 -->
  <meta http-equiv="window-target" content="_top" />
  
  
  <title>JVM学习记录合集 | Skyung-Blog</title>
  <meta name="description" content="JVM学习过程中的记录，参考了视频https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV1yE411Z7AP?share_source&#x3D;copy_web">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM学习记录合集">
<meta property="og:url" content="https://skyung.gitee.io/Skyung/2022/02/27/JVM/index.html">
<meta property="og:site_name" content="Skyung-Blog">
<meta property="og:description" content="JVM学习过程中的记录，参考了视频https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV1yE411Z7AP?share_source&#x3D;copy_web">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150422.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312170046184.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150440.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150534.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312170637088.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312170844024.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312171000970.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312171351435.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312171855315.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312172046779.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150547.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150618.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150630.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150653.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150715.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150736.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150813.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150827.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150842.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150931.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151027.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312192815705.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312192831601.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312192959497.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/2579123-e0b8898d895aee05.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312193506478.png">
<meta property="og:image" content="https://gitee.com/lifuh/picgo/raw/master/img/image-20220310162727033.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151317.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312193745785.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312193755210.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151336.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151346.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151421.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151441.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151459.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151523.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151547.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151605.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151624.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194253723.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194356452.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194407461.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194443799.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200611205050.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200611223153.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200611230658.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200613104723.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312152901018.png">
<meta property="article:published_time" content="2022-02-26T16:00:00.000Z">
<meta property="article:modified_time" content="2022-03-11T16:00:00.000Z">
<meta property="article:author" content="Skyung">
<meta property="article:tag" content="java">
<meta property="article:tag" content="jvm">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150422.png">
  <!-- Canonical links -->
  <link rel="canonical" href="https://skyung.gitee.io/Skyung/2022/02/27/JVM/index.html">
  
    <link rel="alternate" href="/atom.xml" title="Skyung-Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="images/favicon.png" type="image/x-icon">
  
  
<link rel="stylesheet" href="/Skyung/css/style.css">

  
  
  
  
<meta name="generator" content="Hexo 5.4.1"></head>


<body class="main-center" itemscope itemtype="http://schema.org/WebPage">
  <header class="header" itemscope itemtype="http://schema.org/WPHeader">
  <div class="slimContent">
    <div class="navbar-header">
      
      
      <div class="profile-block text-center">
        <a id="avatar" href="https://github.com/cofess" target="_blank">
          <img class="img-circle img-rotate" src="/Skyung/images/avatar.jpg" width="200" height="200">
        </a>
        <h2 id="name" class="hidden-xs hidden-sm">Skyung</h2>
        <h3 id="title" class="hidden-xs hidden-sm hidden-md">Web Developer</h3>
        <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i> Wuhan, China</small>
      </div>
      
      <div class="search" id="search-form-wrap">

    <form class="search-form sidebar-form">
        <div class="input-group">
            <input type="text" class="search-form-input form-control" placeholder="搜索" />
            <span class="input-group-btn">
                <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i class="icon icon-search"></i></button>
            </span>
        </div>
    </form>
    <div class="ins-search">
  <div class="ins-search-mask"></div>
  <div class="ins-search-container">
    <div class="ins-input-wrapper">
      <input type="text" class="ins-search-input" placeholder="想要查找什么..." x-webkit-speech />
      <button type="button" class="close ins-close ins-selectable" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
    </div>
    <div class="ins-section-wrapper">
      <div class="ins-section-container"></div>
    </div>
  </div>
</div>


</div>
      <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
    </div>
    <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
      <ul class="nav navbar-nav main-nav ">
        
        
        <li class="menu-item menu-item-home">
          <a href="/Skyung/.">
            
            <i class="icon icon-home-fill"></i>
            
            <span class="menu-title">首页</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-archives">
          <a href="/Skyung/archives">
            
            <i class="icon icon-archives-fill"></i>
            
            <span class="menu-title">归档</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-categories">
          <a href="/Skyung/categories">
            
            <i class="icon icon-folder"></i>
            
            <span class="menu-title">分类</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-tags">
          <a href="/Skyung/tags">
            
            <i class="icon icon-tags"></i>
            
            <span class="menu-title">标签</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-repository">
          <a href="/Skyung/repository">
            
            <i class="icon icon-project"></i>
            
            <span class="menu-title">项目</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-books">
          <a href="/Skyung/books">
            
            <i class="icon icon-book-fill"></i>
            
            <span class="menu-title">书单</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-links">
          <a href="/Skyung/links">
            
            <i class="icon icon-friendship"></i>
            
            <span class="menu-title">友链</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-about">
          <a href="/Skyung/about">
            
            <i class="icon icon-cup-fill"></i>
            
            <span class="menu-title">关于</span>
          </a>
        </li>
        
      </ul>
      
	
    <ul class="social-links">
    	
        <li><a href="/Skyung/null" target="_blank" title="Github" data-toggle=tooltip data-placement=top><i class="icon icon-github"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Weibo" data-toggle=tooltip data-placement=top><i class="icon icon-weibo"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Twitter" data-toggle=tooltip data-placement=top><i class="icon icon-twitter"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Behance" data-toggle=tooltip data-placement=top><i class="icon icon-behance"></i></a></li>
        
        <li><a href="/Skyung/atom.xml" target="_blank" title="Rss" data-toggle=tooltip data-placement=top><i class="icon icon-rss"></i></a></li>
        
    </ul>

    </nav>
  </div>
</header>

  
    <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    
      <div class="widget">
    <h3 class="widget-title">公告</h3>
    <div class="widget-body">
        <div id="board">
            <div class="content">
                <p>欢迎交流与分享经验!</p>
            </div>
        </div>
    </div>
</div>

    
      
  <div class="widget">
    <h3 class="widget-title">分类</h3>
    <div class="widget-body">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a><span class="category-list-count">20</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title">标签</h3>
    <div class="widget-body">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/java/" rel="tag">java</a><span class="tag-list-count">19</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/jvm/" rel="tag">jvm</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/linux/" rel="tag">linux</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/redis/" rel="tag">redis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" rel="tag">多线程</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" rel="tag">并发编程</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag">设计模式</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E9%9B%B6%E6%95%A3%E7%82%B9/" rel="tag">零散点</a><span class="tag-list-count">1</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title">标签云</h3>
    <div class="widget-body tagcloud">
      <a href="/Skyung/tags/java/" style="font-size: 14px;">java</a> <a href="/Skyung/tags/jvm/" style="font-size: 13px;">jvm</a> <a href="/Skyung/tags/linux/" style="font-size: 13px;">linux</a> <a href="/Skyung/tags/redis/" style="font-size: 13px;">redis</a> <a href="/Skyung/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" style="font-size: 13px;">多线程</a> <a href="/Skyung/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" style="font-size: 13.33px;">并发编程</a> <a href="/Skyung/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" style="font-size: 13.67px;">设计模式</a> <a href="/Skyung/tags/%E9%9B%B6%E6%95%A3%E7%82%B9/" style="font-size: 13px;">零散点</a>
    </div>
  </div>

    
      
  <div class="widget">
    <h3 class="widget-title">归档</h3>
    <div class="widget-body">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/05/">五月 2022</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/04/">四月 2022</a><span class="archive-list-count">7</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/03/">三月 2022</a><span class="archive-list-count">9</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/02/">二月 2022</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2021/09/">九月 2021</a><span class="archive-list-count">1</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget-body">
      <ul class="recent-post-list list-unstyled no-thumbnail">
        
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/08/Spring/" class="title">spring</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-07T16:00:00.000Z" itemprop="datePublished">2022-05-08</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/08/netty-RPC/" class="title">Netty-RPC</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-07T16:00:00.000Z" itemprop="datePublished">2022-05-08</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/07/%E5%B8%B8%E7%94%A8%E7%B1%BB%E5%BA%93%E5%92%8CAPI/" class="title">常见类库</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-06T16:00:00.000Z" itemprop="datePublished">2022-05-07</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/07/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" class="title">单元测试</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-06T16:00:00.000Z" itemprop="datePublished">2022-05-07</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/01/netty%E8%81%8A%E5%A4%A9%E5%AE%A4/" class="title">Netty聊天室</a>
              </p>
              <p class="item-date">
                <time datetime="2022-04-30T16:00:00.000Z" itemprop="datePublished">2022-05-01</time>
              </p>
            </div>
          </li>
          
      </ul>
    </div>
  </div>
  

    
  </div>
</aside>

  
  
  <aside class="sidebar sidebar-toc collapse   in  " id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    <nav id="toc" class="article-toc">
      <h3 class="toc-title">文章目录</h3>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFJVM"><span class="toc-number">1.</span> <span class="toc-text">什么是JVM</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84"><span class="toc-number">2.</span> <span class="toc-text">内存结构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0-%E5%AF%84%E5%AD%98-%E5%99%A8"><span class="toc-number">2.1.</span> <span class="toc-text">程序计数(寄存)器</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89"><span class="toc-number">2.1.1.</span> <span class="toc-text">定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8"><span class="toc-number">2.1.2.</span> <span class="toc-text">作用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81%E6%BC%94%E7%A4%BA"><span class="toc-number">2.1.3.</span> <span class="toc-text">代码演示</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88JVM-Stack"><span class="toc-number">2.2.</span> <span class="toc-text">虚拟机栈JVM Stack</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89-1"><span class="toc-number">2.2.1.</span> <span class="toc-text">定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81%E6%BC%94%E7%A4%BA-1"><span class="toc-number">2.2.2.</span> <span class="toc-text">代码演示</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%97%AE%E9%A2%98%E8%BE%A8%E6%9E%90"><span class="toc-number">2.2.3.</span> <span class="toc-text">问题辨析</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%88%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA"><span class="toc-number">2.2.4.</span> <span class="toc-text">栈内存溢出</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E8%BF%90%E8%A1%8C%E8%AF%8A%E6%96%AD"><span class="toc-number">2.2.5.</span> <span class="toc-text">线程运行诊断</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88"><span class="toc-number">2.3.</span> <span class="toc-text">本地方法栈</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86Heap"><span class="toc-number">2.4.</span> <span class="toc-text">堆Heap</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89-2"><span class="toc-number">2.4.1.</span> <span class="toc-text">定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA"><span class="toc-number">2.4.2.</span> <span class="toc-text">堆内存溢出</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E5%86%85%E5%AD%98%E8%AF%8A%E6%96%AD"><span class="toc-number">2.4.3.</span> <span class="toc-text">堆内存诊断</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E5%8C%BA"><span class="toc-number">2.5.</span> <span class="toc-text">方法区</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89-3"><span class="toc-number">2.5.1.</span> <span class="toc-text">定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BB%93%E6%9E%84"><span class="toc-number">2.5.2.</span> <span class="toc-text">结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%83%E7%A9%BA%E9%97%B4%E6%BA%A2%E5%87%BA"><span class="toc-number">2.5.3.</span> <span class="toc-text">元空间溢出</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0"><span class="toc-number">2.5.4.</span> <span class="toc-text">运行时常量池</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%B2%E6%B1%A0StringTable"><span class="toc-number">2.5.5.</span> <span class="toc-text">串池StringTable</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#intern%E6%96%B9%E6%B3%95-1-8"><span class="toc-number">2.5.5.1.</span> <span class="toc-text">intern方法 1.8</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#intern%E6%96%B9%E6%B3%95-1-6"><span class="toc-number">2.5.5.2.</span> <span class="toc-text">intern方法 1.6</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#StringTable-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="toc-number">2.5.5.3.</span> <span class="toc-text">StringTable 垃圾回收</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#StringTable%E8%B0%83%E4%BC%98"><span class="toc-number">2.5.5.4.</span> <span class="toc-text">StringTable调优</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%87%8A%E6%94%BE%E5%8E%9F%E7%90%86"><span class="toc-number">2.5.6.</span> <span class="toc-text">释放原理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E6%98%BE%E5%BC%8F%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="toc-number">2.5.7.</span> <span class="toc-text">禁止显式的垃圾回收</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="toc-number">3.</span> <span class="toc-text">垃圾回收</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E5%AF%B9%E8%B1%A1%E5%8F%AF%E4%BB%A5%E5%9B%9E%E6%94%B6"><span class="toc-number">3.0.1.</span> <span class="toc-text">如何判断对象可以回收</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%B3%95"><span class="toc-number">3.0.1.1.</span> <span class="toc-text">引用计数法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90%E7%AE%97%E6%B3%95"><span class="toc-number">3.0.1.2.</span> <span class="toc-text">可达性分析算法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B"><span class="toc-number">3.0.1.3.</span> <span class="toc-text">四种引用类型</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BC%95%E7%94%A8%E9%98%9F%E5%88%97"><span class="toc-number">3.0.1.4.</span> <span class="toc-text">引用队列</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95"><span class="toc-number">3.0.2.</span> <span class="toc-text">垃圾回收算法</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4"><span class="toc-number">3.0.2.1.</span> <span class="toc-text">标记-清除</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A0%87%E8%AE%B0-%E6%95%B4%E7%90%86"><span class="toc-number">3.0.2.2.</span> <span class="toc-text">标记-整理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%A4%8D%E5%88%B6"><span class="toc-number">3.0.2.3.</span> <span class="toc-text">复制</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%88%86%E4%BB%A3%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="toc-number">3.0.2.4.</span> <span class="toc-text">分代垃圾回收</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%9B%B8%E5%85%B3JVM%E5%8F%82%E6%95%B0"><span class="toc-number">3.0.2.5.</span> <span class="toc-text">相关JVM参数</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA"><span class="toc-number">3.0.3.</span> <span class="toc-text">线程的内存溢出</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="toc-number">3.0.4.</span> <span class="toc-text">垃圾回收器</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%B8%B2%E8%A1%8C"><span class="toc-number">3.0.4.1.</span> <span class="toc-text">串行</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%90%9E%E5%90%90%E9%87%8F%E4%BC%98%E5%85%88"><span class="toc-number">3.0.4.2.</span> <span class="toc-text">吞吐量优先</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E6%97%B6%E9%97%B4%E4%BC%98%E5%85%88"><span class="toc-number">3.0.4.3.</span> <span class="toc-text">响应时间优先</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#G1"><span class="toc-number">3.0.4.4.</span> <span class="toc-text">G1</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#G1%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8%E7%9A%84%E4%BC%98%E5%8C%96"><span class="toc-number">3.0.4.5.</span> <span class="toc-text">G1垃圾回收器的优化</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E8%B0%83%E4%BC%98"><span class="toc-number">3.0.5.</span> <span class="toc-text">垃圾回收调优</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%B0%83%E4%BC%98%E9%A2%86%E5%9F%9F"><span class="toc-number">3.0.5.1.</span> <span class="toc-text">调优领域</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%A1%AE%E5%AE%9A%E7%9B%AE%E6%A0%87"><span class="toc-number">3.0.5.2.</span> <span class="toc-text">确定目标</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%9C%80%E5%BF%AB%E7%9A%84GC%E6%98%AF%E4%B8%8D%E5%8F%91%E7%94%9FGC"><span class="toc-number">3.0.5.3.</span> <span class="toc-text">最快的GC是不发生GC</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%96%B0%E7%94%9F%E4%BB%A3%E8%B0%83%E4%BC%98"><span class="toc-number">3.0.5.4.</span> <span class="toc-text">新生代调优</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%80%81%E5%B9%B4%E4%BB%A3%E8%B0%83%E4%BC%98"><span class="toc-number">3.0.5.5.</span> <span class="toc-text">老年代调优</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90"><span class="toc-number">3.0.5.6.</span> <span class="toc-text">案例分析</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%92%8C%E5%AD%97%E8%8A%82%E7%A0%81%E6%8A%80%E6%9C%AF"><span class="toc-number">4.</span> <span class="toc-text">类加载和字节码技术</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B1%BB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84"><span class="toc-number">4.1.</span> <span class="toc-text">类文件结构</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%AD%94%E6%95%B0"><span class="toc-number">4.1.1.</span> <span class="toc-text">魔数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%89%88%E6%9C%AC"><span class="toc-number">4.1.2.</span> <span class="toc-text">版本</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B8%B8%E9%87%8F%E6%B1%A0"><span class="toc-number">4.1.3.</span> <span class="toc-text">常量池</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%AE%BF%E9%97%AE%E6%A0%87%E8%AF%86%E4%B8%8E%E7%BB%A7%E6%89%BF%E4%BF%A1%E6%81%AF"><span class="toc-number">4.1.4.</span> <span class="toc-text">访问标识与继承信息</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Field-%E4%BF%A1%E6%81%AF"><span class="toc-number">4.1.5.</span> <span class="toc-text">Field 信息</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Method-%E4%BF%A1%E6%81%AF"><span class="toc-number">4.1.6.</span> <span class="toc-text">Method 信息</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%99%84%E5%8A%A0%E5%B1%9E%E6%80%A7"><span class="toc-number">4.1.7.</span> <span class="toc-text">附加属性</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E8%8A%82%E7%A0%81%E6%8C%87%E4%BB%A4"><span class="toc-number">4.2.</span> <span class="toc-text">字节码指令</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#javap-%E5%B7%A5%E5%85%B7"><span class="toc-number">4.2.1.</span> <span class="toc-text">javap 工具</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B"><span class="toc-number">4.2.2.</span> <span class="toc-text">方法执行流程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD%E6%8C%87%E4%BB%A4"><span class="toc-number">4.2.3.</span> <span class="toc-text">条件判断指令</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BE%AA%E7%8E%AF%E6%8E%A7%E5%88%B6%E6%8C%87%E4%BB%A4"><span class="toc-number">4.2.4.</span> <span class="toc-text">循环控制指令</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95"><span class="toc-number">4.2.5.</span> <span class="toc-text">构造方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8"><span class="toc-number">4.2.6.</span> <span class="toc-text">方法调用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A4%9A%E6%80%81%E5%8E%9F%E7%90%86"><span class="toc-number">4.2.7.</span> <span class="toc-text">多态原理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86"><span class="toc-number">4.2.8.</span> <span class="toc-text">异常处理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Synchronized"><span class="toc-number">4.2.9.</span> <span class="toc-text">Synchronized</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BC%96%E8%AF%91%E6%9C%9F%E5%A4%84%E7%90%86"><span class="toc-number">4.3.</span> <span class="toc-text">编译期处理</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%BB%98%E8%AE%A4%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0"><span class="toc-number">4.3.1.</span> <span class="toc-text">默认构造函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%8A%A8%E6%8B%86%E8%A3%85%E7%AE%B1"><span class="toc-number">4.3.2.</span> <span class="toc-text">自动拆装箱</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B3%9B%E5%9E%8B%E9%9B%86%E5%90%88%E5%8F%96%E5%80%BC"><span class="toc-number">4.3.3.</span> <span class="toc-text">泛型集合取值</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0"><span class="toc-number">4.3.4.</span> <span class="toc-text">可变参数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#foreach"><span class="toc-number">4.3.5.</span> <span class="toc-text">foreach</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#switch%E5%AD%97%E7%AC%A6%E4%B8%B2"><span class="toc-number">4.3.6.</span> <span class="toc-text">switch字符串</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#switch%E6%9E%9A%E4%B8%BE"><span class="toc-number">4.3.7.</span> <span class="toc-text">switch枚举</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9E%9A%E4%B8%BE%E7%B1%BB"><span class="toc-number">4.3.8.</span> <span class="toc-text">枚举类</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#try-with-resources"><span class="toc-number">4.3.9.</span> <span class="toc-text">try-with-resources</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99%E6%97%B6%E7%9A%84%E6%A1%A5%E6%8E%A5%E6%96%B9%E6%B3%95"><span class="toc-number">4.3.10.</span> <span class="toc-text">方法重写时的桥接方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8C%BF%E5%90%8D%E5%86%85%E9%83%A8%E7%B1%BB"><span class="toc-number">4.3.11.</span> <span class="toc-text">匿名内部类</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E9%98%B6%E6%AE%B5"><span class="toc-number">4.4.</span> <span class="toc-text">类加载阶段</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8A%A0%E8%BD%BD"><span class="toc-number">4.4.1.</span> <span class="toc-text">加载</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%93%BE%E6%8E%A5"><span class="toc-number">4.4.2.</span> <span class="toc-text">链接</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%AA%8C%E8%AF%81"><span class="toc-number">4.4.2.1.</span> <span class="toc-text">验证</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%87%86%E5%A4%87"><span class="toc-number">4.4.2.2.</span> <span class="toc-text">准备</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%A7%A3%E6%9E%90"><span class="toc-number">4.4.2.3.</span> <span class="toc-text">解析</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="toc-number">4.4.3.</span> <span class="toc-text">初始化</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="toc-number">4.5.</span> <span class="toc-text">类加载器</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%BC%8F"><span class="toc-number">4.5.1.</span> <span class="toc-text">双亲委派模式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E4%B8%8A%E4%B8%8B%E6%96%87%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="toc-number">4.5.2.</span> <span class="toc-text">线程上下文类加载器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="toc-number">4.5.3.</span> <span class="toc-text">自定义类加载器</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%90%E8%A1%8C%E6%9C%9F%E4%BC%98%E5%8C%96"><span class="toc-number">4.6.</span> <span class="toc-text">运行期优化</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8D%B3%E6%97%B6%E7%BC%96%E8%AF%91"><span class="toc-number">4.6.1.</span> <span class="toc-text">即时编译</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%88%86%E5%B1%82%E7%BC%96%E8%AF%91"><span class="toc-number">4.6.1.1.</span> <span class="toc-text">分层编译</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90"><span class="toc-number">4.6.1.2.</span> <span class="toc-text">逃逸分析</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E5%86%85%E8%81%94"><span class="toc-number">4.6.1.3.</span> <span class="toc-text">方法内联</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%AD%97%E6%AE%B5%E4%BC%98%E5%8C%96"><span class="toc-number">4.6.1.4.</span> <span class="toc-text">字段优化</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%8D%E5%B0%84%E4%BC%98%E5%8C%96"><span class="toc-number">4.6.2.</span> <span class="toc-text">反射优化</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"><span class="toc-number">5.</span> <span class="toc-text">内存模型</span></a></li></ol>
    </nav>
  </div>
</aside>

<main class="main" role="main">
  <div class="content">
  <article id="post-JVM" class="article article-type-post" itemscope itemtype="http://schema.org/BlogPosting">
    
    <div class="article-header">
      
        
  
    <h1 class="article-title" itemprop="name">
      JVM学习记录合集
    </h1>
  

      
      <div class="article-meta">
        <span class="article-date">
    <i class="icon icon-calendar-check"></i>
	<a href="/Skyung/2022/02/27/JVM/" class="article-date">
	  <time datetime="2022-02-26T16:00:00.000Z" itemprop="datePublished">2022-02-27</time>
	</a>
</span>
        
  <span class="article-category">
    <i class="icon icon-folder"></i>
    <a class="article-category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
  </span>

        
  <span class="article-tag">
    <i class="icon icon-tags"></i>
	<a class="article-tag-link-link" href="/Skyung/tags/java/" rel="tag">java</a>, <a class="article-tag-link-link" href="/Skyung/tags/jvm/" rel="tag">jvm</a>
  </span>


        

        <span class="post-comment"><i class="icon icon-comment"></i> <a href="/Skyung/2022/02/27/JVM/#comments" class="article-comment-link">评论</a></span>
        
      </div>
    </div>
    <div class="article-entry marked-body" itemprop="articleBody">
      
        <h2 id="什么是JVM"><a href="#什么是JVM" class="headerlink" title="什么是JVM"></a>什么是JVM</h2><p>[JDK1.8的JVM官方文档] (<a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se8/html/index.html">https://docs.oracle.com/javase/specs/jvms/se8/html/index.html</a>)</p>
<p><strong>定义</strong>：Java Virtual Machine，JAVA程序的<strong>运行环境</strong>（JAVA二进制字节码的运行环境）</p>
<p><strong>好处</strong></p>
<ul>
<li>一次编写，到处运行；</li>
<li>自动内存管理，垃圾回收机制；</li>
<li>数组下标越界检查</li>
<li>虚方法表实现多态</li>
</ul>
<p><strong>比较</strong></p>
<p>JVM JRE JDK的区别</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150422.png" alt="img"></p>
<p><strong>常见的JVM</strong></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312170046184.png" alt="image-20220312170046184"></p>
<p><strong>整体架构</strong></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150440.png" alt="img"></p>
<ul>
<li><p>类加载器：加载字节码文件</p>
</li>
<li><p>内存结构</p>
</li>
<li><p>执行引擎：解释器对方法的代码进行执行</p>
<p>java源代码—二进制字节码（jvm指令）—解释器—机器码—CPU</p>
</li>
</ul>
<h2 id="内存结构"><a href="#内存结构" class="headerlink" title="内存结构"></a>内存结构</h2><h3 id="程序计数-寄存-器"><a href="#程序计数-寄存-器" class="headerlink" title="程序计数(寄存)器"></a>程序计数(寄存)器</h3><h4 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h4><p>JVM中的程序计数寄存器（Program Counter Register），Register的命名源于CPU的寄存器，JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。</p>
<p>它是一块很小的内存空间，几乎可以忽略不记。也是运行速度最快的存储区域。</p>
<h4 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h4><p>用来存储指向下一条指令的地址，也即将要执行的指令代码。由执行引擎读取下一条指令。</p>
<p>它是程序控制流的指示器，分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。</p>
<h4 id="代码演示"><a href="#代码演示" class="headerlink" title="代码演示"></a>代码演示</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PCRegisterTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">20</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> i + j;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后将代码进行编译成字节码文件，再次查看 ，发现在字节码的左边有一个行号标识就是指令地址（偏移地址），用于指向当前执行到哪里。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">0: bipush        10</span><br><span class="line">2: istore_1</span><br><span class="line">3: bipush        20</span><br><span class="line">5: istore_2</span><br><span class="line">6: iload_1</span><br><span class="line">7: iload_2</span><br><span class="line">8: iadd</span><br><span class="line">9: istore_3</span><br><span class="line">10: return</span><br></pre></td></tr></table></figure>

<p>当第一条jvm指令被解释器执行后，会返回下一条指令的偏移地址到寄存器，进而读取下一条指令。</p>
<p><strong>为什么要设计PC寄存器</strong></p>
<p>CPU需要不停的切换各个线程，这时候切换回来以后，就得知道接着从哪开始继续执行。JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。</p>
<p><strong>特点</strong>：线程私有，与线程同生命周期；没有内存溢出。</p>
<h3 id="虚拟机栈JVM-Stack"><a href="#虚拟机栈JVM-Stack" class="headerlink" title="虚拟机栈JVM Stack"></a>虚拟机栈JVM Stack</h3><h4 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h4><ul>
<li>每个<strong>线程</strong>运行需要的内存空间，称为<strong>虚拟机栈</strong></li>
<li>每个栈由多个<strong>栈帧</strong>组成，对应着每次调用方法时所占用的内存，比如方法的参数、局部变量，返回地址</li>
<li>每个线程只能有<strong>一个活动栈帧</strong>，对应着<strong>当前正在执行的方法</strong></li>
</ul>
<p>线程每执行一个方法，就把对应的方法的栈帧存放到虚拟机栈，方法调用完时移出栈。</p>
<h4 id="代码演示-1"><a href="#代码演示-1" class="headerlink" title="代码演示"></a>代码演示</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		method1();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">		method2(<span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">method2</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line">		<span class="keyword">return</span> c;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150534.png" alt="img"></p>
<h4 id="问题辨析"><a href="#问题辨析" class="headerlink" title="问题辨析"></a>问题辨析</h4><ul>
<li><p>垃圾回收是否涉及栈内存？</p>
<p>不需要。因为虚拟机栈中是由一个个栈帧组成的，在方法执行完毕后，对应的栈帧就会被弹出栈。所以无需通过垃圾回收机制去回收内存。（垃圾回收针对堆内存）</p>
</li>
<li><p>栈内存的分配越大越好吗？</p>
<p>不是。因为<strong>物理内存是一定的</strong>，栈内存越大，可以支持更多的递归调用，但是<strong>可执行的线程数就会越少</strong>。</p>
<p>-Xssxx 为linux系统设定栈内存大小，默认1m</p>
</li>
<li><p>方法内的局部变量是否是线程安全的？</p>
<p>如果方法内局部变量没有逃离方法的作用范围，则是线程安全的。如果如果局部变量引用了对象，并逃离了方法的作用范围，则需要考虑线程安全问题</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//线程安全</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">m1</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">    sb.append(<span class="number">1</span>);</span><br><span class="line">    sb.append(<span class="number">2</span>);</span><br><span class="line">    System.out.println(sb.toString());</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//有外部参数，线程不安全</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">m2</span><span class="params">(StringBuilder sb)</span> &#123;</span><br><span class="line">    sb.append(<span class="number">1</span>);</span><br><span class="line">    sb.append(<span class="number">2</span>);</span><br><span class="line">    System.out.println(sb.toString());</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//有返回值，线程不安全</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> StringBuilder <span class="title function_">m3</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">    sb.append(<span class="number">1</span>);</span><br><span class="line">    sb.append(<span class="number">2</span>);</span><br><span class="line">    <span class="keyword">return</span> sb;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="栈内存溢出"><a href="#栈内存溢出" class="headerlink" title="栈内存溢出"></a>栈内存溢出</h4><p>Java.lang.stackOverflowError 栈内存溢出</p>
<p><strong>发生的原因</strong></p>
<ul>
<li>虚拟机栈中栈帧过多（无限递归）</li>
<li>每个栈帧所占用过大</li>
</ul>
<h4 id="线程运行诊断"><a href="#线程运行诊断" class="headerlink" title="线程运行诊断"></a>线程运行诊断</h4><p><strong>案例1：CPU占用过高。</strong></p>
<p>Linux环境下运行某些程序的时候，可能导致CPU的占用过高，这时需要定位占用CPU过高的线程。</p>
<ul>
<li><p>top命令，查看是哪个进程占用CPU过高</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312170637088.png" alt="image-20220312170637088"></p>
</li>
<li><p>ps H -eo pid, tid（线程id）, %cpu | grep 刚才通过top查到的进程号。通过ps命令进一步查看是哪个线程占用CPU过高</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312170844024.png" alt="image-20220312170844024"></p>
</li>
<li><p>jstack 进程id，通过查看进程中的线程的nid，刚才通过ps命令看到的tid来对比定位，注意jstack查找出的线程id是16进制的，需要转换</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312171000970.png" alt="image-20220312171000970"></p>
</li>
</ul>
<p>通过线程id32655变成16进制，定位到Thread1第8行代码有问题。</p>
<p><strong>案例2：不返回运行结果</strong></p>
<p>可能是线程发生死锁。</p>
<p>jstack 进程id查看线程信息</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312171351435.png" alt="image-20220312171351435"></p>
<p>上图中线程1和线程2相互死锁。</p>
<h3 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h3><p>给本地方法的运行提供内存空间。</p>
<p>一些带有<strong>native关键字</strong>的方法就是需要JAVA去调用本地的C或者C++方法，java需要与一些底层系统如操做系统或某些硬件交换信息时，所以需要用到本地方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Object类中有很多本地方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">native</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span>;</span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">native</span> Object <span class="title function_">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title function_">notify</span><span class="params">()</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="堆Heap"><a href="#堆Heap" class="headerlink" title="堆Heap"></a>堆Heap</h3><h4 id="定义-2"><a href="#定义-2" class="headerlink" title="定义"></a>定义</h4><p>通过new关键字创建的对象都会被放在堆内存。</p>
<p><strong>特点</strong></p>
<ul>
<li><strong>所有线程共享</strong>，堆内存中的对象都需要<strong>考虑线程安全问题</strong></li>
<li>有垃圾回收机制</li>
</ul>
<h4 id="堆内存溢出"><a href="#堆内存溢出" class="headerlink" title="堆内存溢出"></a>堆内存溢出</h4><p>java.lang.OutofMemoryError ：java heap space. 堆内存溢出</p>
<p><strong>-Xmx</strong>大小设置虚拟机堆内存大小。可以在Ideal里面VM options 填写参数</p>
<p><strong>案例演示</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="type">String</span> <span class="variable">a</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            list.add(a); </span><br><span class="line">            a = a + a;  </span><br><span class="line">            i++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        System.out.println(i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="堆内存诊断"><a href="#堆内存诊断" class="headerlink" title="堆内存诊断"></a>堆内存诊断</h4><ol>
<li><p>jps。查看当前系统中有哪些java进程</p>
</li>
<li><p><strong>jmap</strong> -heap进程id。查看堆内存占用情况。</p>
<p>jdk11的改成了jhsdb jmap –heap –pid 进程id</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">Heap Configuration:</span><br><span class="line">   MinHeapFreeRatio         = 40</span><br><span class="line">   MaxHeapFreeRatio         = 70</span><br><span class="line">   MaxHeapSize              = 4125097984 (3934.0MB)</span><br><span class="line">   NewSize                  = 1363144 (1.2999954223632812MB)</span><br><span class="line">   MaxNewSize               = 2474639360 (2360.0MB)</span><br><span class="line">   OldSize                  = 5452592 (5.1999969482421875MB)</span><br><span class="line">   NewRatio                 = 2</span><br><span class="line">   SurvivorRatio            = 8</span><br><span class="line">   MetaspaceSize            = 21807104 (20.796875MB)</span><br><span class="line">   CompressedClassSpaceSize = 1073741824 (1024.0MB)</span><br><span class="line">   MaxMetaspaceSize         = 17592186044415 MB</span><br><span class="line">   G1HeapRegionSize         = 1048576 (1.0MB)</span><br><span class="line"></span><br><span class="line">Heap Usage:</span><br><span class="line">G1 Heap:</span><br><span class="line">   regions  = 3934</span><br><span class="line">   capacity = 4125097984 (3934.0MB)</span><br><span class="line">   used     = 16777216 (16.0MB)</span><br><span class="line">   free     = 4108320768 (3918.0MB)</span><br><span class="line">   0.4067107269954245% used</span><br><span class="line">G1 Young Generation:</span><br><span class="line">Eden Space:</span><br><span class="line">   regions  = 6</span><br><span class="line">   capacity = 27262976 (26.0MB)</span><br><span class="line">   used     = 6291456 (6.0MB)</span><br><span class="line">   free     = 20971520 (20.0MB)</span><br><span class="line">   23.076923076923077% used</span><br><span class="line">Survivor Space:</span><br><span class="line">   regions  = 0</span><br><span class="line">   capacity = 0 (0.0MB)</span><br><span class="line">   used     = 0 (0.0MB)</span><br><span class="line">   free     = 0 (0.0MB)</span><br><span class="line">   0.0% used</span><br><span class="line">G1 Old Generation:</span><br><span class="line">   regions  = 12</span><br><span class="line">   capacity = 230686720 (220.0MB)</span><br><span class="line">   used     = 10485760 (10.0MB)</span><br><span class="line">   free     = 220200960 (210.0MB)</span><br><span class="line">   4.545454545454546% used</span><br></pre></td></tr></table></figure></li>
<li><p><strong>jconsole</strong>工具。图形界面，多功能的监测工具，可以连续监测。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312171855315.png" alt="image-20220312171855315"></p>
</li>
<li><p><strong>jvirsalvm</strong>工具，类似jconsole。</p>
<p>通过堆dump抓取内存快照，可以查看某一时刻的堆里内存信息，有哪些对象，并按大小排序。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312172046779.png" alt="image-20220312172046779"></p>
</li>
</ol>
<p>比如上图中arraylist数组占用内存过大。还可以点开查看对象的具体信息。</p>
<h3 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h3><h4 id="定义-3"><a href="#定义-3" class="headerlink" title="定义"></a>定义</h4><p>JVM官方文档中</p>
<p>The Java Virtual Machine has a <em>method area</em> that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. <strong>It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods</strong> (<a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9">§2.9</a>) used in class and instance initialization and interface initialization.</p>
<p>The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. </p>
<ul>
<li>储存类结构，例如运行时常量池、字段和方法数据，以及方法和构造函数的代码，包括类和实例初始化以及接口初始化中使用的特殊方法。</li>
<li>方法区线程共享</li>
<li>Hotspot虚拟机jdk7以前，方法区的实现是永久代，jdk8开始方法区的实现使用元空间。元空间不在虚拟机设置的内存中，而是使用<strong>本地内存</strong>。</li>
</ul>
<p>元数据区大小可以使用参数-XX:MetaspaceSize=xx和-XX:MaxMetaspaceSize=xx指定。<em>XX:MetaspaceSize是21M，-XX:MaxMetaspaceSize = -1 即没有限制，也就是说，默认情况下，虚拟机会耗尽所有可用的系统内存</em></p>
<h4 id="结构"><a href="#结构" class="headerlink" title="结构"></a>结构</h4><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150547.png" alt="img"></p>
<p>需要注意的是jdk1.6中的字符串常量池在方法区，1.8以后在堆。</p>
<p><strong>代码验证</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在jdk6下设置 -XX:MaxPermSize=10m</span></span><br><span class="line"><span class="comment">//jdk1.6报错java.lang.OutofMemoryError ：Metaspace</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    List&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&gt;();</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">260000</span>; j++) &#123;</span><br><span class="line">            list.add(String.valueOf(j).intern());</span><br><span class="line">            i++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        System.out.println(i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//在jdk8下报错java.lang.OutofMemoryError ：GC overhead limit exceeded</span></span><br><span class="line"><span class="comment">//设置-Xmx10m -XX:-UseGCOverheadLimit关闭开关后</span></span><br><span class="line"><span class="comment">//报错java.lang.OutofMemoryError ：Java heap space</span></span><br></pre></td></tr></table></figure>

<h4 id="元空间溢出"><a href="#元空间溢出" class="headerlink" title="元空间溢出"></a>元空间溢出</h4><p>java.lang.OutofMemoryError ：Metaspace</p>
<p>演示</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo1_8</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span> &#123; <span class="comment">// 可以用来加载类的二进制字节码</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Demo1_8</span> <span class="variable">test</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo1_8</span>();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++, j++) &#123;</span><br><span class="line">                <span class="comment">// ClassWriter 作用是生成类的二进制字节码</span></span><br><span class="line">                <span class="type">ClassWriter</span> <span class="variable">cw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassWriter</span>(<span class="number">0</span>);</span><br><span class="line">                <span class="comment">// 版本号， public， 类名, 包名, 父类， 接口</span></span><br><span class="line">                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, <span class="string">&quot;Class&quot;</span> + i, <span class="literal">null</span>, <span class="string">&quot;java/lang/Object&quot;</span>, <span class="literal">null</span>);</span><br><span class="line">                <span class="comment">// 返回 byte[]</span></span><br><span class="line">                <span class="type">byte</span>[] code = cw.toByteArray();</span><br><span class="line">                <span class="comment">// 执行了类的加载</span></span><br><span class="line">                test.defineClass(<span class="string">&quot;Class&quot;</span> + i, code, <span class="number">0</span>, code.length); <span class="comment">// Class 对象</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            System.out.println(j);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>实际场景下</strong></p>
<p>spring的动态代理在运行期间经常通过cglib的ClassWriter动态的加载类，使用不当也会导致元空间溢出。</p>
<h4 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h4><p>类编译后的二进制字节码包括：类的基本信息、常量池、类的方法定义（包含了虚拟机指令）</p>
<p>javap -v 类的路径，可以反编译类的字节码，查看类的信息</p>
<ul>
<li><p>类的基本信息</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150618.png" alt="img"></p>
</li>
<li><p>常量池</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150630.png" alt="img"></p>
</li>
<li><p>类的方法</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150653.png" alt="img"></p>
</li>
</ul>
<p><strong>常量池</strong>：就是一张表（如上图中的constant pool）。解释器在执行虚拟机指令时，根指令后的#内容，去常量池表找到要执行的类名、方法名、参数类型、字面量信息。</p>
<p><strong>运行时常量池</strong>：当该类被加载以后，它的常量池信息就会放入运行时常量池，并把里面的符号地址变为真实地址。</p>
<h4 id="串池StringTable"><a href="#串池StringTable" class="headerlink" title="串池StringTable"></a>串池StringTable</h4><p>字符串的加载特点：</p>
<ul>
<li>常量池中信息被加载到运行时常量池时，字符串仅是符号，还不是字符串对象。</li>
<li>字符串在被用到时才开始创建对象。懒加载。</li>
<li>首先会到StringTable（<strong>结构是Hash表</strong>）里面去找是否有相同字符串，没有就把新的字符串放到串池。</li>
<li>利用串池的机制，来避免重复创建字符串对象。</li>
<li>字符串<strong>变量</strong>拼接的原理是StringBuilder。</li>
<li>字符串<strong>常量</strong>拼接的原理是<strong>编译器优化</strong></li>
</ul>
<p><strong>演示字符串加载过程</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StringTableStudy</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">String</span> <span class="variable">a</span> <span class="operator">=</span> <span class="string">&quot;a&quot;</span>; </span><br><span class="line">		<span class="type">String</span> <span class="variable">b</span> <span class="operator">=</span> <span class="string">&quot;b&quot;</span>;</span><br><span class="line">		<span class="type">String</span> <span class="variable">ab</span> <span class="operator">=</span> <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>反编译：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">0: ldc           #2                  // String a</span><br><span class="line">2: astore_1</span><br><span class="line">3: ldc           #3                  // String b</span><br><span class="line">5: astore_2</span><br><span class="line">6: ldc           #4                  // String ab</span><br><span class="line">8: astore_3</span><br><span class="line">9: return</span><br></pre></td></tr></table></figure>

<p>运行时常量池中，但这是a b ab 仅是常量池中的符号，还没有成为java字符串。</p>
<p>当执行到 ldc #2 时，会把符号 a 变为 “a” 字符串对象，并放入串池中。</p>
<p><strong>拼接字符串的过程</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StringTableStudy</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">String</span> <span class="variable">a</span> <span class="operator">=</span> <span class="string">&quot;a&quot;</span>;</span><br><span class="line">		<span class="type">String</span> <span class="variable">b</span> <span class="operator">=</span> <span class="string">&quot;b&quot;</span>;</span><br><span class="line">		<span class="type">String</span> <span class="variable">ab</span> <span class="operator">=</span> <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">		<span class="type">String</span> <span class="variable">ab2</span> <span class="operator">=</span> a+b; <span class="comment">//拼接字符串对象来创建新的字符串</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">ab3</span> <span class="operator">=</span> <span class="string">&quot;a&quot;</span> + <span class="string">&quot;b&quot;</span>;<span class="comment">//使用拼接字符串的方法创建字符串</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>反编译后的结果</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"> 	  Code:</span><br><span class="line">      stack=2, locals=6, args_size=1</span><br><span class="line">         0: ldc           #2                  // String a</span><br><span class="line">         2: astore_1</span><br><span class="line">         3: ldc           #3                  // String b</span><br><span class="line">         5: astore_2</span><br><span class="line">         6: ldc           #4                  // String ab</span><br><span class="line">         8: astore_3</span><br><span class="line">         9: new           #5                  // class java/lang/StringBuilder</span><br><span class="line">        12: dup</span><br><span class="line">        13: invokespecial #6                  // Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">        16: aload_1</span><br><span class="line">        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String</span><br><span class="line">;)Ljava/lang/StringBuilder;</span><br><span class="line">        20: aload_2</span><br><span class="line">        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String</span><br><span class="line">;)Ljava/lang/StringBuilder;</span><br><span class="line">        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/Str</span><br><span class="line">ing;</span><br><span class="line">        27: astore        4</span><br><span class="line">        //ab3初始化时直接从串池中获取字符串</span><br><span class="line">        29: ldc           #4                  // String ab</span><br><span class="line">        31: astore        5</span><br><span class="line">        33: return</span><br></pre></td></tr></table></figure>

<ul>
<li><p>通过拼接字符串变量的方式来创建字符串的过程是:StringBuilder().append(“a”).append(“b”).toString()</p>
<p>toString方法的返回值是一个<strong>新的字符串</strong>，但字符串的值和拼接的字符串一致，但是两个不同的字符串，<strong>一个存在于串池之中，一个存在于堆内存之中</strong>。ab==ab2false。</p>
</li>
<li><p>使用拼接字符串常量的方法来创建新的字符串时，因为内容是常量，javac在编译期会进行优化，结果已在编译期确定为ab，而创建ab的时候已经在串池中放入了“ab”，所以ab3直接从串池中获取值，所以进行的操作和 ab = “ab” 一致。</p>
</li>
</ul>
<p>综合，String str = “abc”和String str3 = new String(“abc”)的实现过程不同。</p>
<h5 id="intern方法-1-8"><a href="#intern方法-1-8" class="headerlink" title="intern方法 1.8"></a>intern方法 1.8</h5><p>调用字符串对象的intern方法，会将该字符串对象尝试放入到串池中。</p>
<ul>
<li>如果串池中没有该字符串对象，则放入成功</li>
<li>如果有该字符串对象，则放入失败</li>
<li>无论放入是否成功，都会返回串池中的字符串对象。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="comment">//&quot;a&quot; &quot;b&quot; 被放入串池中，str则存在于堆内存之中</span></span><br><span class="line">		<span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;a&quot;</span>) + <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">		<span class="comment">//调用str的intern方法，这时串池中没有&quot;ab&quot;，则会将该字符串对象放入到串池中</span></span><br><span class="line">		<span class="type">String</span> <span class="variable">st2</span> <span class="operator">=</span> str.intern();<span class="comment">//所以str2和str都在串池</span></span><br><span class="line">		<span class="comment">//串池中已有&quot;ab&quot;，则直接将串池中的内容返回</span></span><br><span class="line">		<span class="type">String</span> <span class="variable">str3</span> <span class="operator">=</span> <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">		<span class="comment">//都为true</span></span><br><span class="line">		System.out.println(str == st2);</span><br><span class="line">		System.out.println(str == str3);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对比</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">String</span> <span class="variable">str3</span> <span class="operator">=</span> <span class="string">&quot;ab&quot;</span>; <span class="comment">//&quot;ab&quot;放入串池中</span></span><br><span class="line">		<span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;a&quot;</span>) + <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;b&quot;</span>);<span class="comment">//str于堆内存之中</span></span><br><span class="line">        <span class="comment">//&quot;ab&quot;已存在与串池中，所以放入失败，但是会返回串池中的&quot;ab&quot;</span></span><br><span class="line">		<span class="type">String</span> <span class="variable">str2</span> <span class="operator">=</span> str.intern();</span><br><span class="line">		System.out.println(str == str2);<span class="comment">//false</span></span><br><span class="line">		System.out.println(str == str3);<span class="comment">//false</span></span><br><span class="line">		System.out.println(str2 == str3);<span class="comment">//true</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="intern方法-1-6"><a href="#intern方法-1-6" class="headerlink" title="intern方法 1.6"></a>intern方法 1.6</h5><p>调用字符串对象的intern方法，会将该字符串对象尝试放入到串池中</p>
<ul>
<li>如果串池中没有该字符串对象，会将该字符串对象复制一份，再放入到串池中</li>
<li>如果有该字符串对象，则放入失败</li>
</ul>
<p>无论放入是否成功，都会返回<strong>串池中</strong>的字符串对象</p>
<p><strong>注意</strong>：此时无论调用intern方法成功与否，串池中的字符串对象和堆内存中的字符串对象<strong>都不是同一个对象</strong></p>
<h5 id="StringTable-垃圾回收"><a href="#StringTable-垃圾回收" class="headerlink" title="StringTable 垃圾回收"></a>StringTable 垃圾回收</h5><p>查看垃圾回收的信息</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc</span><br></pre></td></tr></table></figure>

<p>StringTable在内存紧张时，会发生垃圾回收</p>
<h5 id="StringTable调优"><a href="#StringTable调优" class="headerlink" title="StringTable调优"></a>StringTable调优</h5><ul>
<li>因为StringTable是由HashTable实现的，所以可以<strong>适当增加HashTable桶的个数</strong>，来减少字符串放入串池所需要的时间。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">  -XX:StringTableSize=桶个数</span><br><span class="line"></span><br><span class="line">- 考虑是否需要将字符串对象入池</span><br><span class="line"></span><br><span class="line">  可以通过intern方法使字符串入池，避免重复的字符串占用太多堆空间。用时间换空间。</span><br><span class="line"></span><br><span class="line">### 直接内存</span><br><span class="line"></span><br><span class="line">- 属于操作系统的内存，常见于NIO操作时，**用于数据缓冲区**</span><br><span class="line">- 不受JVM内存回收管理</span><br><span class="line">- 分配回收成本较高，但读写性能高</span><br><span class="line"></span><br><span class="line">#### 文件读写流程</span><br><span class="line"></span><br><span class="line">```java</span><br><span class="line">private static void io() &#123;</span><br><span class="line">    long start = System.nanoTime();</span><br><span class="line">    try (FileInputStream from = new FileInputStream(文件位置);</span><br><span class="line">         FileOutputStream to = new FileOutputStream(文件位置);</span><br><span class="line">        ) &#123;</span><br><span class="line">        byte[] buf = new byte[1024*1024];</span><br><span class="line">        while (true) &#123;</span><br><span class="line">            int len = from.read(buf);</span><br><span class="line">            if (len == -1) &#123;</span><br><span class="line">                break;</span><br><span class="line">            &#125;</span><br><span class="line">            to.write(buf, 0, len);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; catch (IOException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    long end = System.nanoTime();</span><br><span class="line">    System.out.println(&quot;io 用时：&quot; + (end - start) / 1000_000.0);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<p>java的io读取会调用系统的本地方法。</p>
<p>文件读取时会从磁盘读取到系统内存的缓冲区，再从系统内存读取到java内存的缓冲区，也就是代码中的byte[] buf = new byte[_1Mb];</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150715.png" alt="img"></p>
<p><strong>使用了DirectBuffer</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">private static void directBuffer() &#123;</span><br><span class="line">    long start = System.nanoTime();</span><br><span class="line">    try (FileChannel from = new FileInputStream(文件位置).getChannel();</span><br><span class="line">         FileChannel to = new FileOutputStream(文件位置).getChannel();</span><br><span class="line">        ) &#123;</span><br><span class="line">        ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024);</span><br><span class="line">        while (true) &#123;</span><br><span class="line">            int len = from.read(bb);</span><br><span class="line">            if (len == -1) &#123;</span><br><span class="line">                break;</span><br><span class="line">            &#125;</span><br><span class="line">            bb.flip();</span><br><span class="line">            to.write(bb);</span><br><span class="line">            bb.clear();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; catch (IOException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    long end = System.nanoTime();</span><br><span class="line">    System.out.println(&quot;directBuffer 用时：&quot; + (end - start) / 1000_000.0);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150736.png" alt="img"></p>
<p>直接内存是操作系统和Java代码<strong>都可以访问的一块区域</strong>，无需将代码从系统内存复制到Java堆内存，从而提高了效率。</p>
<h4 id="释放原理"><a href="#释放原理" class="headerlink" title="释放原理"></a>释放原理</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">//通过ByteBuffer申请1M的直接内存</span><br><span class="line">ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Mb);</span><br></pre></td></tr></table></figure>

<p>直接内存的回收不是通过JVM的垃圾回收来释放的，而是通过<strong>unsafe.freeMemory</strong>来手动释放</p>
<p>直接内存也会溢出，报错java.lang.OutofMemoryError ：Direct buffer memory</p>
<p><strong>allocateDirect的实现</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ByteBuffer <span class="title function_">allocateDirect</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DirectByteBuffer</span>(capacity);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>DirectByteBuffer类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">DirectByteBuffer(<span class="type">int</span> cap) &#123;</span><br><span class="line">    <span class="built_in">super</span>(-<span class="number">1</span>, <span class="number">0</span>, cap, cap);</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">pa</span> <span class="operator">=</span> VM.isDirectMemoryPageAligned();</span><br><span class="line">    <span class="type">int</span> <span class="variable">ps</span> <span class="operator">=</span> Bits.pageSize();</span><br><span class="line">    <span class="type">long</span> <span class="variable">size</span> <span class="operator">=</span> Math.max(<span class="number">1L</span>, (<span class="type">long</span>)cap + (pa ? ps : <span class="number">0</span>));</span><br><span class="line">    Bits.reserveMemory(size, cap);</span><br><span class="line">    <span class="type">long</span> <span class="variable">base</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        base = unsafe.allocateMemory(size); <span class="comment">//申请内存</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (OutOfMemoryError x) &#123;</span><br><span class="line">        Bits.unreserveMemory(size, cap);</span><br><span class="line">        <span class="keyword">throw</span> x;</span><br><span class="line">    &#125;</span><br><span class="line">    unsafe.setMemory(base, size, (<span class="type">byte</span>) <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (pa &amp;&amp; (base % ps != <span class="number">0</span>)) &#123;</span><br><span class="line">        <span class="comment">// Round up to page boundary</span></span><br><span class="line">        address = base + ps - (base &amp; (ps - <span class="number">1</span>));</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        address = base;</span><br><span class="line">    &#125;</span><br><span class="line">    cleaner = Cleaner.create(<span class="built_in">this</span>, <span class="keyword">new</span> <span class="title class_">Deallocator</span>(base, size, cap)); </span><br><span class="line">    att = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>申请内存是通过unsafe.allocateMemory(size); unsafe类是系统底层的一个类。</p>
<p>释放内存时，Cleaner.create(this, new Deallocator(base, size, cap))，Cleaner是<strong>虚引用类型</strong>，关联的是this，也就是DirectByteBuffer，当DirectByteBuffer被垃圾回收时，就执行Deallocator任务对象中的run()方法。</p>
<p>Deallocator是一个任务对象，调用UNSAFE.freeMemory(address);方法释放内存。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Deallocator</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> address;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> size;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> capacity;</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Deallocator</span><span class="params">(<span class="type">long</span> address, <span class="type">long</span> size, <span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="keyword">assert</span> (address != <span class="number">0</span>);</span><br><span class="line">        <span class="built_in">this</span>.address = address;</span><br><span class="line">        <span class="built_in">this</span>.size = size;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (address == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        UNSAFE.freeMemory(address);</span><br><span class="line">        address = <span class="number">0</span>;</span><br><span class="line">        Bits.unreserveMemory(size, capacity);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="禁止显式的垃圾回收"><a href="#禁止显式的垃圾回收" class="headerlink" title="禁止显式的垃圾回收"></a>禁止显式的垃圾回收</h4><p>有时候会在代码中执行System.gc(); 显式的垃圾回收。由于垃圾回收会对新生代和老年代都进行回收，比较消耗性能，因此有时会禁止显式的垃圾回收。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">XX:+DisableExplicitGC 显式的</span><br></pre></td></tr></table></figure>

<p>但会引发问题，造成下面代码中的byteBuffer对象没有被垃圾回收，对应的直接内存没有被及时回收。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="type">ByteBuffer</span> <span class="variable">byteBuffer</span> <span class="operator">=</span> ByteBuffer.allocateDirect(_1Gb);</span><br><span class="line">    System.out.println(<span class="string">&quot;分配完毕...&quot;</span>);</span><br><span class="line">    System.in.read();</span><br><span class="line">    System.out.println(<span class="string">&quot;开始释放...&quot;</span>);</span><br><span class="line">    byteBuffer = <span class="literal">null</span>;</span><br><span class="line">    System.gc(); <span class="comment">// 显式的垃圾回收，Full GC</span></span><br><span class="line">    System.in.read();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>禁止显式的垃圾回收对直接内存的回收有影响。</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">_1Gb</span> <span class="operator">=</span> <span class="number">1024</span> * <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="type">Unsafe</span> <span class="variable">unsafe</span> <span class="operator">=</span> getUnsafe();</span><br><span class="line">    <span class="comment">// 分配内存</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">base</span> <span class="operator">=</span> unsafe.allocateMemory(_1Gb);</span><br><span class="line">    unsafe.setMemory(base, _1Gb, (<span class="type">byte</span>) <span class="number">0</span>);</span><br><span class="line">    System.in.read();</span><br><span class="line">    <span class="comment">// 释放内存</span></span><br><span class="line">    unsafe.freeMemory(base);</span><br><span class="line">    System.in.read();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以通过unsafe.freeMemory(base);直接释放内存。</p>
<h2 id="垃圾回收"><a href="#垃圾回收" class="headerlink" title="垃圾回收"></a>垃圾回收</h2><h4 id="如何判断对象可以回收"><a href="#如何判断对象可以回收" class="headerlink" title="如何判断对象可以回收"></a>如何判断对象可以回收</h4><h5 id="引用计数法"><a href="#引用计数法" class="headerlink" title="引用计数法"></a>引用计数法</h5><p>弊端：循环引用时，两个对象的计数都为1，导致两个对象都无法被释放。A引用B，B引用A。</p>
<h5 id="可达性分析算法"><a href="#可达性分析算法" class="headerlink" title="可达性分析算法"></a>可达性分析算法</h5><p>JVM中的垃圾回收器通过<strong>可达性分析</strong>来探索所有存活的对象。</p>
<p>扫描堆中的对象，看能否沿着GC Root对象为起点的引用链找到该对象，如果找不到，则表示可以回收。</p>
<p>可以作为GC Root的对象：</p>
<ul>
<li>虚拟机栈（栈帧中的本地变量表）中引用的对象。<strong>引用存放在栈帧，对象在堆空间。</strong>　</li>
<li>方法区中类静态属性引用的对象</li>
<li>方法区中常量引用的对象</li>
<li>本地方法栈中引用的对象</li>
</ul>
<p>在可达性分析算法中查找存活的对象，首先要找到哪些是GC Roots；有两种查找GC Roots的方法：一种是遍历方法区和栈区来查找（保守式GC）；一种是通过OopMap的数据结构来记录引用的位置（准确式GC）。</p>
<p><strong>OopMap 记录了栈上本地变量到堆上对象的引用关系</strong>。其作用是：垃圾收集时，收集线程会对栈上的内存进行扫描，看看哪些位置存储了 Reference 类型。但问题是，栈上的本地变量表里面只有一部分数据是 Reference 类型的，但不得不对整个栈全部扫描一遍，这是对时间和资源的一种浪费。</p>
<p>用空间换时间，在某个时候把栈上代表引用的位置全部记录下来，这样到真正 gc 的时候就可以直接读取，而不用再一点一点的扫描，HotSpot ，使用 OopMap 的数据结构来记录这类信息。</p>
<p>一个栈帧对应着一个方法，一个方法里面可能有多个安全点。 gc 发生时，程序首先运行到最近的一个安全点停下来，然后更新自己的 OopMap ，记下栈上哪些位置代表着引用。遍历每个栈帧的 OopMap ，通过栈中记录的被引用对象的内存地址，即可找到这些对象（ GC Roots ）。</p>
<p>如在类加载过程中，JIT编译过程中，分别记录下类成员 和调用栈中的引用的调用信息。对应OopMap的位置即可作用一个安全点。线程只有到达安全点时才能暂停下来进行可达性分析。</p>
<h5 id="四种引用类型"><a href="#四种引用类型" class="headerlink" title="四种引用类型"></a>四种引用类型</h5><p>引用——&gt;引用的对象，引用也是一个对象。</p>
<p><strong>强引用</strong>：只有GC Root都不引用该对象时，才会回收强引用对象。</p>
<p><strong>软引用</strong>（SoftReference）：当GC Root指向软引用对象时，在内存不足时，会回收软引用所引用的对象。</p>
<p>一些不重要的数据可以用软硬引用，能避免堆内存溢出。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo1</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="keyword">final</span> <span class="type">int</span> <span class="variable">_4M</span> <span class="operator">=</span> <span class="number">4</span>*<span class="number">1024</span>*<span class="number">1024</span>;</span><br><span class="line">		<span class="comment">//list--&gt;SoftReference--&gt;byte数组</span></span><br><span class="line">		List&lt;SoftReference&lt;<span class="type">byte</span>[]&gt;&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">		SoftReference&lt;<span class="type">byte</span>[]&gt; ref= <span class="keyword">new</span> <span class="title class_">SoftReference</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">byte</span>[_4M]);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在回收软引用所指向的对象时，<strong>软引用本身不会被清理</strong>如果想要清理软引用，需要使<strong>用引用队列</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo2</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">_4MB</span> <span class="operator">=</span> <span class="number">4</span> * <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        List&lt;SoftReference&lt;<span class="type">byte</span>[]&gt;&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 软引用队列</span></span><br><span class="line">        ReferenceQueue&lt;<span class="type">byte</span>[]&gt; queue = <span class="keyword">new</span> <span class="title class_">ReferenceQueue</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            <span class="comment">//关联了引用队列， 当软引用所关联的 byte[]被回收时，软引用自己会加入到 queue中去</span></span><br><span class="line">            SoftReference&lt;<span class="type">byte</span>[]&gt; ref = <span class="keyword">new</span> <span class="title class_">SoftReference</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">byte</span>[_4MB], queue);</span><br><span class="line">            list.add(ref);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 从队列中获取软引用对象，并移除</span></span><br><span class="line">        Reference&lt;? <span class="keyword">extends</span> <span class="title class_">byte</span>[]&gt; poll = queue.poll();</span><br><span class="line">        <span class="keyword">while</span>( poll != <span class="literal">null</span>) &#123;</span><br><span class="line">            list.remove(poll);</span><br><span class="line">            poll = queue.poll();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//此时list队列里面就不会有null的对象</span></span><br><span class="line">        <span class="keyword">for</span> (SoftReference&lt;<span class="type">byte</span>[]&gt; reference : list) &#123;</span><br><span class="line">            System.out.println(reference.get());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>弱引用</strong>(WeakReference)：只有弱引用引用该对象时，在垃圾回收时，无论内存是否充足，都会回收弱引用所引用的对象。</p>
<p>弱引用的使用和软引用类似，只是将 SoftReference 换为了 WeakReference。</p>
<p><strong>虚引用</strong>(PhantomReference)：当虚引用对象所引用的对象被回收以后，虚引用对象就会被放入引用队列中，调用虚引用的方法。</p>
<ul>
<li>虚引用的一个体现是释放直接内存所分配的内存，当引用的对象ByteBuffer被垃圾回收以后，虚引用对象Cleaner就会被放入引用队列中，然后调用Cleaner的clean方法来释放直接内存。</li>
</ul>
<p><strong>终结器引用</strong>(FinalReference)：所有的类都继承自Object类，Object类有一个finalize方法。当某个对象不再被其他的对象所引用时，会先将终结器引用放入引用队列中，然后根据终结器引用对象找到它所引用的对象，然后调用该对象的finalize方法。调用以后，该对象就可以被垃圾回收了。</p>
<h5 id="引用队列"><a href="#引用队列" class="headerlink" title="引用队列"></a>引用队列</h5><ul>
<li>软引用和弱引用<strong>可以配合</strong>引用队列<ul>
<li>在<strong>弱引用</strong>和<strong>虚引用</strong>所引用的对象被回收以后，会将这些引用放入引用队列中，方便一起回收这些软/弱引用对象</li>
</ul>
</li>
<li>虚引用和终结器引用<strong>必须配合</strong>引用队列<ul>
<li>虚引用和终结器引用在使用时会关联一个引用队列</li>
</ul>
</li>
</ul>
<h4 id="垃圾回收算法"><a href="#垃圾回收算法" class="headerlink" title="垃圾回收算法"></a>垃圾回收算法</h4><h5 id="标记-清除"><a href="#标记-清除" class="headerlink" title="标记-清除"></a>标记-清除</h5><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150813.png" alt="img"></p>
<p>垃圾回收时，先采用标记算法确定可回收对象，然后垃圾收集器根据标识清除相应的内容，给堆内存腾出相应的空间。</p>
<ul>
<li>这里的腾出内存空间并不是将内存空间的字节清0，而是记录下这段内存的起始结束地址，下次分配内存的时候，会直接<strong>覆盖</strong>这段内存。</li>
</ul>
<p><strong>缺点</strong>：容易产生大量的内存碎片。</p>
<h5 id="标记-整理"><a href="#标记-整理" class="headerlink" title="标记-整理"></a>标记-整理</h5><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150827.png" alt="img"></p>
<p>将不被GC Root引用的对象回收，清除其占用的内存空间。然后整理剩余的对象，可以有效避免因内存碎片而导致的问题，但是因为整体需要消耗一定的时间，所以效率较低。</p>
<h5 id="复制"><a href="#复制" class="headerlink" title="复制"></a>复制</h5><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150842.png" alt="img"></p>
<p>将内存分为等大小的两个区域，FROM和TO（TO中为空）。先将被GC Root引用的对象从FROM放入TO中，再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题，但是会占用双倍的内存空间。</p>
<h5 id="分代垃圾回收"><a href="#分代垃圾回收" class="headerlink" title="分代垃圾回收"></a>分代垃圾回收</h5><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608150931.png" alt="img"></p>
<p>分代回收器有两个分区：老生代和新生代，新生代默认的空间占比总空间的 1/3，老生代的默认占比是 2/3。</p>
<p>新生代使用的是<strong>复制算法</strong>，新生代里有 3 个分区：Eden、To Survivor、From Survivor，它们的默认占比是 8:1:1，它的执行流程如下：</p>
<ol>
<li>新创建的对象都被放在了Eden中。当Eden中的内存不足时，就会进行一次垃圾回收，这时的回收叫做 <strong>Minor GC</strong>。</li>
<li>Minor GC 把 Eden + From Survivor 存活的对象放入 To Survivor 区；</li>
<li>清空 Eden 和 From Survivor 分区；</li>
<li>From Survivor 和 To Survivor 分区交换，From Survivor 变 To Survivor，To Survivor 变 From Survivor。</li>
<li>每次在 From Survivor 到 To Survivor 移动时都存活的对象，年龄就 +1，当年龄到达 15（默认配置是 15）时，升级为老年代。<strong>大对象无论Eden是否空会直接进入老年代</strong>，新生代满时也会把部分对象放到老年代。</li>
</ol>
<ul>
<li>Minor GC会引发stop the world，暂停其他用户线程，等垃圾回收结束，用户线程才恢复。</li>
<li>老年代当空间占用到达某个值之后就会触发全局垃圾回收<strong>Full GC</strong>，一般使用标记整理的执行算法，STW的时间更长。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。</li>
</ul>
<h5 id="相关JVM参数"><a href="#相关JVM参数" class="headerlink" title="相关JVM参数"></a>相关JVM参数</h5><table>
<thead>
<tr>
<th>含义</th>
<th>参数</th>
</tr>
</thead>
<tbody><tr>
<td>堆初始大小</td>
<td>-Xms</td>
</tr>
<tr>
<td>堆最大大小</td>
<td>-Xmx 或 -XX:MaxHeapSize=size</td>
</tr>
<tr>
<td>新生代大小</td>
<td>-Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )</td>
</tr>
<tr>
<td>幸存区比例（动态）</td>
<td>-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy</td>
</tr>
<tr>
<td>幸存区比例</td>
<td>-XX:SurvivorRatio=ratio</td>
</tr>
<tr>
<td>晋升阈值</td>
<td>-XX:MaxTenuringThreshold=threshold</td>
</tr>
<tr>
<td>晋升详情</td>
<td>-XX:+PrintTenuringDistribution</td>
</tr>
<tr>
<td>GC详情</td>
<td>-XX:+PrintGCDetails -verbose:gc</td>
</tr>
<tr>
<td>FullGC 前 MinorGC</td>
<td>-XX:+ScavengeBeforeFullGC</td>
</tr>
<tr>
<td>打印GC详情</td>
<td>-XX:PrintGCDetails -verbose:gc</td>
</tr>
</tbody></table>
<p>例如：-Xmx3550m -Xms3550m -Xmn2g -Xss128k</p>
<h4 id="线程的内存溢出"><a href="#线程的内存溢出" class="headerlink" title="线程的内存溢出"></a>线程的内存溢出</h4><p>一个线程的内存溢出，不会造成整个java进程的结束。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        ArrayList&lt;<span class="type">byte</span>[]&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        list.add(<span class="keyword">new</span> <span class="title class_">byte</span>[_8MB]);</span><br><span class="line">        list.add(<span class="keyword">new</span> <span class="title class_">byte</span>[_8MB]);</span><br><span class="line">    &#125;).start();</span><br><span class="line">    System.out.println(<span class="string">&quot;sleep....&quot;</span>);</span><br><span class="line">    Thread.sleep(<span class="number">1000L</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当一个线程抛出OOM异常，它所占用的内存资源会被全部释放掉。从而不影响其他线程的工作。</p>
<h4 id="垃圾回收器"><a href="#垃圾回收器" class="headerlink" title="垃圾回收器"></a>垃圾回收器</h4><p>相关概念：</p>
<p><strong>并行收集</strong>：指多条垃圾收集线程并行工作，但此时<strong>用户线程仍处于等待状态</strong>。</p>
<p><strong>并发收集</strong>：指用户线程与垃圾收集线程<strong>并发工作，都可以抢占CPU的运行时间</strong>。</p>
<h5 id="串行"><a href="#串行" class="headerlink" title="串行"></a>串行</h5><ul>
<li>单线程</li>
<li>适宜堆内存较小的个人电脑（CPU核数较少)</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151027.png" alt="img"></p>
<p>Serial + SerialOld则表示在新生代和老年代。</p>
<p><strong>安全点</strong>：OpenJDK官方定义如下：</p>
<ul>
<li>安全点是在程序执行期间的所有GC Root已知并且所有堆对象的内容一致的点。</li>
<li>从全局的角度来看，所有线程必须在GC运行之前在安全点阻塞。</li>
</ul>
<p>因为是串行的，所以只有一个垃圾回收线程。且在该线程执行回收工作时，其他线程进入<strong>阻塞</strong>状态。</p>
<h5 id="吞吐量优先"><a href="#吞吐量优先" class="headerlink" title="吞吐量优先"></a>吞吐量优先</h5><ul>
<li>多线程</li>
<li>堆内存较大，多核cpu</li>
<li>让单位时间内，STW 的时间最短 0.2 0.2 = 0.4，垃圾回收时间占比最低，这样就称吞吐量高</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312192815705.png" alt="image-20220312192815705"></p>
<p>-XX:+UseParallelGC ~ -XX:+UseParallelOldGC 开启并行的垃圾回收器</p>
<p>-XX:+UseAdaptiveSizePolicy 自适应Eden、From 和 To 区的大小</p>
<p>-XX:GCTimeRatio=ratio 调整垃圾回收时间和总时间的占比   1/(1+ratio)默认是99</p>
<p>-XX:MaxGCPauseMillis=ms 暂定的最大毫秒数，默认200ms(与上一个目标是冲突的)</p>
<p>-XX:ParallelGCThreads=n  ParallelGC运行的线程数</p>
<h5 id="响应时间优先"><a href="#响应时间优先" class="headerlink" title="响应时间优先"></a>响应时间优先</h5><ul>
<li>多线程</li>
<li>堆内存较大，多核cpu</li>
<li>尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5</li>
<li>JDK1.8默认使用的垃圾回收器，工作在老年代。</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312192831601.png" alt="image-20220312192831601"></p>
<p>-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld 开启并发的垃圾回收器cms<br>-XX:ConcGCThreads=threads 垃圾回收的线程数，一般设为CPU核心数的1/4<br>-XX:CMSInitiatingOccupancyFraction=percent 执行垃圾回收的内存占比，因为是并发回收，gc的过程中会产生浮动垃圾，需要预留空间。<br>-XX:+CMSScavengeBeforeRemark  开启在标记前先对新生代进行垃圾回收，新生代会引用老年代，可以减少老年代的GC时间。</p>
<p><strong>cms用的标记-清除算法。</strong>当老年代碎片过多造成内存不足时，新生代的对象无法正常进入老年代，此时cms垃圾器会<strong>退化为SerialOld垃圾回收器</strong>。</p>
<h5 id="G1"><a href="#G1" class="headerlink" title="G1"></a>G1</h5><p>定义：Garbage First。并发的垃圾回收器，JDK9的默认垃圾回收器，取代CMS.</p>
<p><strong>适用场景:</strong></p>
<ul>
<li>同时注重吞吐量（Throughput）和低延迟（Low latency），默认的暂停目标是200 ms </li>
<li>超大堆内存，会将堆划分为多个大小相等的 Region</li>
<li>整体上是 标记+整理 算法，两个区域之间是复制算法</li>
</ul>
<p><strong>相关JVM参数</strong></p>
<p>-XX:+UseG1GC</p>
<p>-XX:G1HeapRegionSize=size</p>
<p>-XX:MaxGCPauseMillis=time</p>
<p><strong>G1 垃圾回收阶段</strong></p>
<p>Young Collection—Young Collection + CM— Mixed Collection三个阶段循环。</p>
<p>(1)Young Collection</p>
<ul>
<li>会 STW</li>
</ul>
<p>把新生代中Edn区域的对象复制到幸存区。</p>
<p>当幸存区的对象较多或者年龄较大，再次触发垃圾回收，年龄大的复制到老年代，不够年龄的复制到另外的幸存区。</p>
<p>（2）Young Collection + CM</p>
<ul>
<li>在 Young GC 时（会stw）会<strong>对 GC Root 进行初始标记</strong></li>
<li>在老年代占用堆内存的比例达到阈值时，对进行CM<strong>并发标记</strong>（<strong>不会STW</strong>），阈值可以根据用户来进行设定</li>
</ul>
<p>-XX:InitiatingHeapOccupancyPercent=percent （默认45%）</p>
<p>(3)Mixed Collection</p>
<p>会对 E、S、O 进行全面垃圾回收</p>
<ul>
<li>最终标记（Remark）会 STW</li>
<li>拷贝存活（Evacuation）会 STW</li>
</ul>
<p>-XX:MaxGCPauseMillis=ms</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312192959497.png" alt="image-20220312192959497"></p>
<p>新生代中Edn中的存活对象复制到幸存区，幸存区的存活对象复制到新的幸存区和老年代，老年代幸存对象复制到新的老年代。为了达到暂停时间最短，会优先收集老年代中垃圾最多的区域，所以叫Garbage First。</p>
<p><strong>辨析</strong></p>
<ul>
<li>SerialGC <ul>
<li>新生代内存不足发生的垃圾收集 minor gc </li>
<li>老年代内存不足发生的垃圾收集 - full gc（新生代老年代一起回收）</li>
</ul>
</li>
<li>ParallelGC <ul>
<li>新生代内存不足发生的垃圾收集 - minor gc </li>
<li>老年代内存不足发生的垃圾收集 - full gc </li>
</ul>
</li>
<li>CMS <ul>
<li>新生代内存不足发生的垃圾收集 - minor gc </li>
<li>老年代内存不足，当并发失败，也就是并发收集垃圾速度赶不上垃圾生成的速度，才会full gc</li>
</ul>
</li>
<li>G1<ul>
<li>新生代内存不足发生的垃圾收集 - minor gc </li>
<li>老年代内存不足，并发标记混合收集，当并发失败才会full gc</li>
</ul>
</li>
</ul>
<p><strong>Young Collection 跨代引用</strong></p>
<p>新生代回收的跨代引用（老年代引用新生代）问题。有的新生代对象的根对象在老年代，如果遍历整个老年代根对象，显然效率会非常低。</p>
<p>相关概念</p>
<ul>
<li><strong>Card Table</strong>（卡表，多种垃圾回收器均具备）。每一个老年代Region，又被分成了固定大小的若干张卡(Card)。每一张卡，都用一个Byte来记录是否修改过，卡表即这些byte的集合。如果一个Card中的对象有引用指向Young区，则将其标记为Dirty Card，下次需要进行YoungGC时，只需要去扫描Dirty Card即可。</li>
<li><strong>RSet</strong>(Remembered Set，记忆集)。是辅助GC过程的一种结构，典型的空间换时间工具。逻辑上说每个Region都有一个RSet，RSet记录了其他Region中的对象引用本Region中对象的关系，属于points-into结构（谁引用了我的对象）。使得垃圾回收器不需要扫描整个堆找到谁引用当前分区中的对象，只需要扫描RSet即可。</li>
<li>CSet(Collection Set)。一组可被回收的分区Region的集合, 是多个对象的集合内存区域。</li>
</ul>
<p>Card Table则是一种points-out（我引用了谁的对象）的结构，每个Card 覆盖一定范围的Heap（一般为512Bytes）。G1的RSet是在Card Table的基础上实现的。这个RSet其实是一个Hash Table，Key是别的Region的起始地址，Value是一个集合，里面的元素是Card Table的Index集合。</p>
<ul>
<li><p>更新Card Table中的引用关系靠post-write barrier和Dirty Card queue。(异步操作)</p>
</li>
<li><p>Concurrent refinement threads更新Remembered Set。</p>
</li>
</ul>
<p>一个Region可能有多个线程在并发修改，因此它们也会并发修改RS，为了避免这样一种冲突，G1垃圾回收器进一步把RS划分成了多个哈希表。每一个线程都在各自的哈希表里面修改。</p>
<p>整个关系如下：</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/2579123-e0b8898d895aee05.png" alt="img"></p>
<p><strong>Remark</strong></p>
<p>G1会有并发标记CM和重标记两个阶段。在并发标记过程中，有可能对象B被标记为垃圾，但该处理过程还未结束，在处理过程结束之前A引用了B，那么回收对象B会造成问题，这时就会用到remark。</p>
<p>pre-write barrier + satb_mark_queue</p>
<p>过程为：</p>
<ul>
<li>在对象引用改变之前，采用写屏障，表示未处理完毕；同时将对象存入一个引用队列进行处理。</li>
<li>在并发标记阶段结束以后，重新标记阶段会STW，然后将放在该队列中的对象重新处理。</li>
</ul>
<h5 id="G1垃圾回收器的优化"><a href="#G1垃圾回收器的优化" class="headerlink" title="G1垃圾回收器的优化"></a>G1垃圾回收器的优化</h5><p><strong>JDK8u20 字符串去重</strong></p>
<p>-XX:+UseStringDeduplication开启</p>
<ul>
<li>优点：节省大量内存</li>
<li>缺点：略微多占用了 cpu 时间，新生代回收时间略微增加</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;hello&quot;</span>); <span class="comment">// char[]&#123;&#x27;h&#x27;,&#x27;e&#x27;,&#x27;l&#x27;,&#x27;l&#x27;,&#x27;o&#x27;&#125;</span></span><br><span class="line"><span class="type">String</span> <span class="variable">s2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">&quot;hello&quot;</span>); <span class="comment">// char[]&#123;&#x27;h&#x27;,&#x27;e&#x27;,&#x27;l&#x27;,&#x27;l&#x27;,&#x27;o&#x27;&#125;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>将所有新分配的字符串放入一个队列</li>
<li>当新生代回收时，G1并发检查是否有字符串重复</li>
<li>如果它们值一样，让两个<strong>字符串对象</strong>引用同一个 <strong>char[]</strong></li>
</ul>
<p>注意，与 String.intern() 不一样：String.intern() 关注的是字符串对象，而字符串去重关注的是 char[]。</p>
<p> <strong>JDK 8u40 并发标记类卸载</strong></p>
<p>所有对象都经过并发标记后，就能知道哪些类不再被使用，当一个类加载器的所有类都不再使用，则卸 载它所加载的所有类。</p>
<p>XX:+ClassUnloadingWithConcurrentMark 默认启用</p>
<p><strong>JDK 8u60 回收巨型对象</strong></p>
<ul>
<li>一个对象大于 region 的一半时，称之为巨型对象</li>
<li>G1 不会对巨型对象进行拷贝，回收时被优先考虑</li>
<li>G1 会跟踪老年代对象对巨型对象的引用（卡表），引用为0 的巨型对象可以在新生 代垃圾回收时处理掉</li>
</ul>
<p><strong>JDK 9 并发标记起始时间的调整</strong></p>
<ul>
<li><p>并发标记必须在堆空间占满前完成，否则退化为 FullGC</p>
</li>
<li><p>JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent，阈值是固定的</p>
</li>
<li><p>JDK 9 可以实现动态调整：</p>
<ul>
<li>XX:InitiatingHeapOccupancyPercent 用来设置初始值</li>
<li>进行数据采样并动态调整</li>
<li>会添加一个安全的空档空间，避免并发垃圾回收退化为 FullGC</li>
</ul>
</li>
</ul>
<p><strong>JDK 9 更高效的回收</strong></p>
<p><a target="_blank" rel="noopener" href="https://docs.oracle.com/en/java/javase/12/gctuning">https://docs.oracle.com/en/java/javase/12/gctuning</a></p>
<h4 id="垃圾回收调优"><a href="#垃圾回收调优" class="headerlink" title="垃圾回收调优"></a>垃圾回收调优</h4><p><a target="_blank" rel="noopener" href="https://docs.oracle.com/en/java/javase/11/tools/">Java Platform, Standard Edition Tools Reference, Release 11 (oracle.com)</a></p>
<p>查看虚拟机参数命令</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;D:\Program Files\Java\jdk-11.0.9\bin\java&quot; -XX:+PrintFlagsFinal -version | findstr &quot;GC&quot;</span><br></pre></td></tr></table></figure>

<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312193506478.png" alt="image-20220312193506478"></p>
<p>打印垃圾回收信息XX:-PrintGCDetails</p>
<p><strong>预备知识</strong></p>
<ul>
<li>掌握 GC 相关的 VM 参数，会基本的空间调整</li>
<li>掌握相关工具 jmap等</li>
<li>明白一点：调优跟应用、环境有关，没有放之四海而皆准的法则</li>
</ul>
<h5 id="调优领域"><a href="#调优领域" class="headerlink" title="调优领域"></a>调优领域</h5><ul>
<li>内存</li>
<li>锁竞争</li>
<li>CPU占用</li>
<li>IO</li>
<li>GC</li>
</ul>
<p><strong>GC调优只是调优领域的一方面。</strong></p>
<h5 id="确定目标"><a href="#确定目标" class="headerlink" title="确定目标"></a>确定目标</h5><p>低延迟/高吞吐量？ 选择合适的GC</p>
<ul>
<li>CMS G1 ZGC</li>
<li>ParallelGC</li>
<li>其他虚拟机的Zing</li>
</ul>
<h5 id="最快的GC是不发生GC"><a href="#最快的GC是不发生GC" class="headerlink" title="最快的GC是不发生GC"></a>最快的GC是不发生GC</h5><p>首先排除减少因为自身编写的代码而引发的内存问题。</p>
<p>查看Full GC前后的内存占用，考虑以下几个问题</p>
<ul>
<li><p>数据是不是太多？比如resultSet = statement.executeQuery(“select * from 大表”)查了整个表的数据</p>
</li>
<li><p>数据表示是否太臃肿</p>
<ul>
<li>对象图，不要查无用的数据</li>
<li>对象大小 Integer 24字节 int 4字节</li>
</ul>
</li>
<li><p>是否存在内存泄漏</p>
<ul>
<li>比如定义了static Map不断的放数据。</li>
<li>长时间的存活对象可用软、弱引用</li>
<li>一些缓存数据可考虑第三方缓存实现，Redis、ehcache（直接在jvm虚拟机中缓存，速度快，效率高；但是缓存共享麻烦，集群分布式应用不方便）</li>
</ul>
</li>
</ul>
<h5 id="新生代调优"><a href="#新生代调优" class="headerlink" title="新生代调优"></a>新生代调优</h5><p>新生代的特点</p>
<ul>
<li>所有的 new 操作的内存分配在Edn，效率很高<ul>
<li>TLAB thread-local allocation buffer，内存的分配也会有线程的并发安全问题，所以就会有一个缓冲区，线程私有。</li>
</ul>
</li>
<li>死亡对象的回收代价是零</li>
<li>大部分对象用过即死</li>
<li> Minor GC 的时间远远低于 Full GC</li>
</ul>
<p>新生代越大越好吗？</p>
<p>-Xmn</p>
<p>Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery). GC is performed in this region more often than in other regions. If the size for the young generation is too small, then a lot of minor garbage collections are performed. If the size is too large, then only full garbage collections are performed, which can take a long time to complete. Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.</p>
<p>新生代太小，容易频繁 Minor GC会STW，新生代太大会使老年代过小，容易full GC。</p>
<p>新生代的设置大小可以考虑：</p>
<ul>
<li><p>新生代能容纳所有【并发量 * (请求-响应)】的数据吗，比如一次请求响应占用500K内存，一秒并发1000次。</p>
</li>
<li><p>幸存区大到能保留【当前活跃对象+需要晋升对象】，避免由于新生代太小导致存活时间短的对象晋升到了老年代。</p>
</li>
<li><p>晋升阈值配置得当，让长时间存活对象尽快晋升</p>
<ul>
<li><p>-XX:MaxTenuringThreshold=threshold 调整最大晋升阈值</p>
</li>
<li><p>-XX:+PrintTenuringDistribution  打印晋升信息</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Desired survivor size 48286924 bytes, new threshold 10 (max 10)</span><br><span class="line">- age 1: 28992024 bytes, 28992024 total</span><br><span class="line">- age 2: 1366864 bytes, 30358888 total</span><br><span class="line">- age 3: 1425912 bytes, 31784800 total</span><br><span class="line">...</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h5 id="老年代调优"><a href="#老年代调优" class="headerlink" title="老年代调优"></a>老年代调优</h5><p>以 CMS 为例 </p>
<ul>
<li><p>CMS 的老年代内存越大越好，避免浮动垃圾造成并发回收失败</p>
</li>
<li><p>先尝试不做调优，如果没有 Full GC 那么已经很ok， 有Full GC 也是先尝试调优新生代</p>
</li>
<li><p>观察发生 Full GC 时老年代内存占用，将老年代内存预设调大 1/4 ~ 1/3</p>
<ul>
<li>XX:CMSInitiatingOccupancyFraction=percent 老年代开启垃圾回收的阈值</li>
</ul>
</li>
</ul>
<h5 id="案例分析"><a href="#案例分析" class="headerlink" title="案例分析"></a>案例分析</h5><ul>
<li><p>案例1 Full GC 和 Minor GC频繁</p>
<p>增大新生代内存</p>
</li>
<li><p>案例2 请求高峰期发生 Full GC，单次暂停时间特别长 （CMS） </p>
<p>CMS垃圾回收分四个阶段，查看哪个阶段耗时长，通常重新标记耗时长，由于重新标记会扫描整个堆内存，新生代对象多会导致重标记时间长。-XX:+CMSScavengeBeforeRemark  开启在标记前先对新生代进行垃圾回收。</p>
</li>
<li><p>案例3 老年代充裕情况下，发生 Full GC （CMS jdk1.7）</p>
<p>方法区在永久代，永久代空间不足也会 Full GC</p>
</li>
</ul>
<h2 id="类加载和字节码技术"><a href="#类加载和字节码技术" class="headerlink" title="类加载和字节码技术"></a>类加载和字节码技术</h2><h3 id="类文件结构"><a href="#类文件结构" class="headerlink" title="类文件结构"></a>类文件结构</h3><p>执行javac X:…\XXX.java获得.class字节码文件</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// HelloWorld 示例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloWorld</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;hello world&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译为 HelloWorld.class 后是这个样子的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09</span><br><span class="line">0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07</span><br><span class="line">0000040 00 1c 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29</span><br><span class="line">0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e</span><br><span class="line">0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63</span><br><span class="line">0000120 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01</span><br><span class="line">0000140 00 04 74 68 69 73 01 00 1d 4c 63 6e 2f 69 74 63</span><br><span class="line">0000160 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c 6f</span><br><span class="line">0000200 57 6f 72 6c 64 3b 01 00 04 6d 61 69 6e 01 00 16</span><br><span class="line">0000220 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72</span><br><span class="line">0000240 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13</span><br><span class="line">0000260 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69</span><br><span class="line">0000300 6e 67 3b 01 00 10 4d 65 74 68 6f 64 50 61 72 61</span><br><span class="line">0000320 6d 65 74 65 72 73 01 00 0a 53 6f 75 72 63 65 46</span><br><span class="line">0000340 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64</span><br><span class="line">0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e</span><br><span class="line">0000400 00 1f 01 00 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64</span><br><span class="line">0000420 07 00 20 0c 00 21 00 22 01 00 1b 63 6e 2f 69 74</span><br><span class="line">0000440 63 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c</span><br><span class="line">0000460 6f 57 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61</span><br><span class="line">0000500 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61</span><br><span class="line">0000520 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f</span><br><span class="line">0000540 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72</span><br><span class="line">0000560 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76</span><br><span class="line">0000600 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d</span><br><span class="line">0000620 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a</span><br><span class="line">0000640 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b</span><br><span class="line">0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01</span><br><span class="line">0000700 00 07 00 08 00 01 00 09 00 00 00 2f 00 01 00 01</span><br><span class="line">0000720 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 0a 00</span><br><span class="line">0000740 00 00 06 00 01 00 00 00 04 00 0b 00 00 00 0c 00</span><br><span class="line">0000760 01 00 00 00 05 00 0c 00 0d 00 00 00 09 00 0e 00</span><br><span class="line">0001000 0f 00 02 00 09 00 00 00 37 00 02 00 01 00 00 00</span><br><span class="line">0001020 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 02 00 0a</span><br><span class="line">0001040 00 00 00 0a 00 02 00 00 00 06 00 08 00 07 00 0b</span><br><span class="line">0001060 00 00 00 0c 00 01 00 00 00 09 00 10 00 11 00 00</span><br><span class="line">0001100 00 12 00 00 00 05 01 00 10 00 00 00 01 00 13 00</span><br><span class="line">0001120 00 00 02 00 14</span><br></pre></td></tr></table></figure>

<p>根据 JVM 规范，类文件结构如下</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">u4 magic; 前四个字节，魔术</span><br><span class="line">u2 minor_version; 两个字节小版本号</span><br><span class="line">u2 major_version;  主版本号</span><br><span class="line">u2 constant_pool_count; 常量池</span><br><span class="line">cp_info constant_pool[constant_pool_count-1];</span><br><span class="line">u2 access_flags; 访问修饰</span><br><span class="line">u2 this_class; 类的包名类名</span><br><span class="line">u2 super_class; 父类</span><br><span class="line">u2 interfaces_count; 接口信息</span><br><span class="line">u2 interfaces[interfaces_count];</span><br><span class="line">u2 fields_count; 成员变量信息</span><br><span class="line">field_info fields[fields_count];</span><br><span class="line">u2 methods_count; 方法信息</span><br><span class="line">method_info methods[methods_count];</span><br><span class="line">u2 attributes_count; 附加的属性信息</span><br><span class="line">attribute_info attributes[attributes_count];</span><br></pre></td></tr></table></figure>

<h4 id="魔数"><a href="#魔数" class="headerlink" title="魔数"></a>魔数</h4><p>0~3 字节，表示它是否是【class】类型的文件</p>
<p>0000000 <strong>ca fe ba be</strong> 00 00 00 34 00 23 0a 00 06 00 15 09</p>
<h4 id="版本"><a href="#版本" class="headerlink" title="版本"></a>版本</h4><p>4~7 字节，表示类的版本 00 34（52） 表示是 Java 8</p>
<p>0000000 ca fe ba be <strong>00 00 00 34</strong> 00 23 0a 00 06 00 15 09</p>
<h4 id="常量池"><a href="#常量池" class="headerlink" title="常量池"></a>常量池</h4><p>8<del>9 字节，表示常量池长度，00 23 （35） 表示常量池有 #1</del>#34项，注意 #0 项不计入，也没有值 0000000 ca fe ba be 00 00 00 34 <strong>00 23</strong> 0a 00 06 00 15 09</p>
<p>第#1项 0a 表示一个 Method 信息，00 06 和 00 15（21） 表示它引用了常量池中 #6 和 #21 项来获得 这个方法的【所属类】和【方法名】 0000000 ca fe ba be 00 00 00 34 00 23 <strong>0a 00 06 00 15</strong> 09</p>
<p>第#2项 09 表示一个 Field 信息，00 16（22）和 00 17（23） 表示它引用了常量池中 #22 和 # 23 项 来获得这个成员变量的【所属类】和【成员变量名】</p>
<p>0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 <strong>09</strong></p>
<p>0000020 <strong>00 16 00 17</strong> 08 00 18 0a 00 19 00 1a 07 00 1b 07</p>
<p>…</p>
<p>第#6项 07 表示一个 Class 信息，00 1c（28） 表示它引用了常量池中 #28 项 </p>
<p>0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b <strong>07</strong> 0000040 <strong>00 1c</strong> 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29</p>
<p>第#19项 01 表示一个 utf8 串，00 0a（10） 表示长度，是【SourceFile】 </p>
<p>0000320 6d 65 74 65 72 73 <strong>01 00 0a 53 6f 75 72 63 65 46</strong> </p>
<p>0000340 <strong>69 6c 65</strong> 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64</p>
<p>第#28项 01 表示一个 utf8 串，00 10（16） 表示长度，是【java/lang/Object】</p>
<p>0000460 6f 57 6f 72 6c 64 01 <strong>00 10</strong> <strong>6a 61 76 61 2f 6c 61</strong></p>
<p>0000500 <strong>6e 67 2f 4f 62 6a 65 63 74</strong> 01 00 10 6a 61 76 61</p>
<p><img src="https://gitee.com/lifuh/picgo/raw/master/img/image-20220310162727033.png" alt="image-20220310162727033"></p>
<p>查看某一项是什么类型，查表：</p>
<table>
<thead>
<tr>
<th>Constant Type</th>
<th>Constant Type</th>
</tr>
</thead>
<tbody><tr>
<td>CONSTANT_Class</td>
<td>7</td>
</tr>
<tr>
<td>CONSTANT_Fieldref</td>
<td>9</td>
</tr>
<tr>
<td>CONSTANT_Methodref</td>
<td>10</td>
</tr>
<tr>
<td>CONSTANT_InterfaceMethodref</td>
<td>11</td>
</tr>
<tr>
<td>CONSTANT_String</td>
<td>8</td>
</tr>
<tr>
<td>CONSTANT_Integer</td>
<td>3</td>
</tr>
<tr>
<td>CONSTANT_Float</td>
<td>4</td>
</tr>
<tr>
<td>CONSTANT_Long</td>
<td>5</td>
</tr>
<tr>
<td>CONSTANT_Double</td>
<td>6</td>
</tr>
<tr>
<td>CONSTANT_NameAndType</td>
<td>12</td>
</tr>
<tr>
<td>CONSTANT_Utf8</td>
<td>1</td>
</tr>
<tr>
<td>CONSTANT_MethodHandle</td>
<td>15</td>
</tr>
<tr>
<td>CONSTANT_MethodType</td>
<td>16</td>
</tr>
<tr>
<td>CONSTANT_InvokeDynamic</td>
<td>18</td>
</tr>
</tbody></table>
<h4 id="访问标识与继承信息"><a href="#访问标识与继承信息" class="headerlink" title="访问标识与继承信息"></a>访问标识与继承信息</h4><p>将常量池顺着读完，就是访问标识与继承信息</p>
<p>21 表示该 class 是一个类，公共的</p>
<p>0000660 29 56 <strong>00 21</strong> 00 05 00 06 00 00 00 00 00 02 00 01</p>
<p>05 表示根据常量池中 #5 找到本类全限定名</p>
<p>0000660 29 56 00 21 <strong>00 05</strong> 00 06 00 00 00 00 00 02 00 01</p>
<p>06 表示根据常量池中 #6 找到父类全限定名</p>
<p>0000660 29 56 00 21 00 05 <strong>00 06</strong> 00 00 00 00 00 02 00 01</p>
<p>表示接口的数量，本类为 0</p>
<p>0000660 29 56 00 21 00 05 00 06 <strong>00 00</strong> 00 00 00 02 00 01</p>
<table>
<thead>
<tr>
<th>Flag Name</th>
<th>Value</th>
<th>Interpretation</th>
</tr>
</thead>
<tbody><tr>
<td>ACC_PUBLIC</td>
<td>0x0001</td>
<td>Declared public ; may be accessed from outside its  package</td>
</tr>
<tr>
<td>ACC_FINAL</td>
<td>0x0010</td>
<td>Declared final ; no subclasses allowed.</td>
</tr>
<tr>
<td>ACC_SUPER</td>
<td>0x0020</td>
<td>Treat superclass methods specially when invoked by the invokespecial instruction.</td>
</tr>
<tr>
<td>ACC_INTERFACE</td>
<td>0x0200</td>
<td>Is an interface, not a class.</td>
</tr>
<tr>
<td>ACC_ABSTRACT</td>
<td>0x0400</td>
<td>Declared abstract ; must not be instantiated.</td>
</tr>
<tr>
<td>ACC_SYNTHETIC</td>
<td>0x1000</td>
<td>Declared synthetic; not present in the source code.</td>
</tr>
<tr>
<td>ACC_ANNOTATION</td>
<td>0x2000</td>
<td>Declared as an annotation type.</td>
</tr>
<tr>
<td>ACC_ENUM</td>
<td>0x4000</td>
<td>Declared as an enum type.</td>
</tr>
</tbody></table>
<p>例如上面的00 21 就是0001 和0020相加，表示是个公共的类。</p>
<h4 id="Field-信息"><a href="#Field-信息" class="headerlink" title="Field 信息"></a>Field 信息</h4><p>表示成员变量数量，本类为 0</p>
<p>0000660 29 56 00 21 00 05 00 06 00 00 <strong>00 00</strong> 00 02 00 01</p>
<table>
<thead>
<tr>
<th>FieldType</th>
<th>Type</th>
</tr>
</thead>
<tbody><tr>
<td>B</td>
<td>byte</td>
</tr>
<tr>
<td>C</td>
<td>char</td>
</tr>
<tr>
<td>D</td>
<td>double</td>
</tr>
<tr>
<td>F</td>
<td>float</td>
</tr>
<tr>
<td>I</td>
<td>int</td>
</tr>
<tr>
<td>J</td>
<td>long</td>
</tr>
<tr>
<td>L ClassName ;</td>
<td>reference: an instance of class ClassName</td>
</tr>
<tr>
<td>S</td>
<td>short</td>
</tr>
<tr>
<td>Z</td>
<td>boolean</td>
</tr>
<tr>
<td>[</td>
<td>reference: one array dimension</td>
</tr>
</tbody></table>
<h4 id="Method-信息"><a href="#Method-信息" class="headerlink" title="Method 信息"></a>Method 信息</h4><p>表示方法数量，本类为 2</p>
<p>0000660 29 56 00 21 00 05 00 06 00 00 00 00 <strong>00 02</strong> 00 01</p>
<p>一个方法由<strong>访问修饰符，名称，参数描述，方法属性数量，方法属性组成</strong></p>
<ul>
<li>00 01代表访问修饰符（本类中是 public）</li>
<li>00 07代表引用了常量池 #07 项作为方法名称</li>
<li>00 08代表引用了常量池 #08 项作为方法参数描</li>
<li>00 01代表方法属性数量，本方法是 1</li>
<li>接下来是方法属性<ul>
<li>00 09 表示引用了常量池 #09 项，发现是【Code】属性</li>
<li>00 00 00 2f 表示此属性的长度是 47，往后数47个</li>
<li>包含：操作数栈最大深度、局部变量表最大槽（slot）数、字节码长度，字节码长度，方法细节属性数量、引用的常量池，局部变量的名称，类型，生命周期等。</li>
</ul>
</li>
</ul>
<p>0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 <strong>00 01</strong></p>
<p>0000700 00 07 00 08 00 01 00 09 00 00 00 2f 00 01 00 01</p>
<p>0000720 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 0a 00</p>
<p>0000740 00 00 06 00 01 00 00 00 04 00 0b 00 00 00 0c 00</p>
<p>0000760 <strong>01 00 00 00 05 00 0c 00 0d 00 00 00</strong> 09 00 0e 00</p>
<h4 id="附加属性"><a href="#附加属性" class="headerlink" title="附加属性"></a>附加属性</h4><p>00 01 表示附加属性数量</p>
<p>00 13 表示引用了常量池 #19 项，即【SourceFile】</p>
<p>00 00 00 02 表示此属性的长度</p>
<p>00 14 表示引用了常量池 #20 项，即【HelloWorld.java】</p>
<p>0001100 00 12 00 00 00 05 01 00 10 00 00 <strong>00 01</strong> 00 13 00</p>
<p>0001120 00 00 02 00 14</p>
<p><strong>参考文献</strong></p>
<p><a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html">https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html</a></p>
<h3 id="字节码指令"><a href="#字节码指令" class="headerlink" title="字节码指令"></a>字节码指令</h3><p>HelloWorld(); 构造方法的字节码指令</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2a b7 00 01 b1</span><br></pre></td></tr></table></figure>

<p>2a =&gt; aload_0 加载 slot 0 的局部变量，即 this，做为下面的 invokespecial 构造方法调用的参数</p>
<p> b7 =&gt; invokespecial 预备调用构造方法</p>
<p> 00 01 引用常量池中 #1 项，即【 Method java/lang/Object.””:()V 】</p>
<p> b1 表示返回</p>
<p>另一个是 public static void main(java.lang.String[]); 主方法的字节码指令</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">b2 00 02 12 03 b6 00 04 b1</span><br></pre></td></tr></table></figure>

<p>b2 =&gt; getstatic 用来加载静态变量</p>
<p>00 02 引用常量池中 #2 项，即【Field java/lang/System.out:Ljava/io/PrintStream;】</p>
<p>12 =&gt; ldc 加载参数</p>
<p>03 引用常量池中 #3 项，即 【String hello world】</p>
<p>b6 =&gt; invokevirtual 预备调用成员方法</p>
<p>00 04 引用常量池中 #4 项，即【Method java/io/PrintStream.println:(Ljava/lang/String;)V】 </p>
<p>b1 表示返回</p>
<p>官方文档 <a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5">https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5</a></p>
<h4 id="javap-工具"><a href="#javap-工具" class="headerlink" title="javap 工具"></a>javap 工具</h4><p>自己分析类文件结构太麻烦了，Oracle 提供了 javap 工具来反编译 class 文件</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">javap -v F:\xxx\Main.class</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line">Classfile /F:/xxx/Demo1.class</span><br><span class="line">  Last modified 2020-6-6; size 434 bytes</span><br><span class="line">  MD5 checksum df1dce65bf6fb0b4c1de318051f4a67e</span><br><span class="line">  Compiled from &quot;Demo1.java&quot;</span><br><span class="line">public class com.nyima.JVM.day5.Demo1</span><br><span class="line">  minor version: 0</span><br><span class="line">  major version: 52</span><br><span class="line">  flags: ACC_PUBLIC, ACC_SUPER</span><br><span class="line">Constant pool: </span><br><span class="line">   #1 = Methodref          #6.#15         // java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">   #3 = String             #18            // hello world</span><br><span class="line">   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line">   #5 = Class              #21            // com/nyima/JVM/day5/Demo1</span><br><span class="line">   #6 = Class              #22            // java/lang/Object</span><br><span class="line">   #7 = Utf8               &lt;init&gt;</span><br><span class="line">   #8 = Utf8               ()V</span><br><span class="line">   #9 = Utf8               Code</span><br><span class="line">  #10 = Utf8               LineNumberTable</span><br><span class="line">  #11 = Utf8               main</span><br><span class="line">  #12 = Utf8               ([Ljava/lang/String;)V</span><br><span class="line">  #13 = Utf8               SourceFile</span><br><span class="line">  #14 = Utf8               Demo1.java</span><br><span class="line">  #15 = NameAndType        #7:#8          // &quot;&lt;init&gt;&quot;:()V</span><br><span class="line">  #16 = Class              #23            // java/lang/System</span><br><span class="line">  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;</span><br><span class="line">  #18 = Utf8               hello world</span><br><span class="line">  #19 = Class              #26            // java/io/PrintStream</span><br><span class="line">  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V</span><br><span class="line">  #21 = Utf8               com/nyima/JVM/day5/Demo1</span><br><span class="line">  #22 = Utf8               java/lang/Object</span><br><span class="line">  #23 = Utf8               java/lang/System</span><br><span class="line">  #24 = Utf8               out</span><br><span class="line">  #25 = Utf8               Ljava/io/PrintStream;</span><br><span class="line">  #26 = Utf8               java/io/PrintStream</span><br><span class="line">  #27 = Utf8               println</span><br><span class="line">  #28 = Utf8               (Ljava/lang/String;)V</span><br><span class="line">&#123;</span><br><span class="line">  public com.nyima.JVM.day5.Demo1();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=1, locals=1, args_size=1</span><br><span class="line">         0: aload_0</span><br><span class="line">         1: invokespecial #1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">         4: return</span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line 7: 0              //line 7是代码行号，0是字节码行号</span><br><span class="line"></span><br><span class="line">  public static void main(java.lang.String[]);</span><br><span class="line">    descriptor: ([Ljava/lang/String;)V</span><br><span class="line">    flags: ACC_PUBLIC, ACC_STATIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=2, locals=1, args_size=1</span><br><span class="line">         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">         3: ldc           #3                  // String hello world</span><br><span class="line">         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line"></span><br><span class="line">         8: return</span><br><span class="line">      LineNumberTable:</span><br><span class="line">        line 9: 0</span><br><span class="line">        line 10: 8</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="方法执行流程"><a href="#方法执行流程" class="headerlink" title="方法执行流程"></a>方法执行流程</h4><p>原始java代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo3_1</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> Short.MAX_VALUE + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line">        System.out.println(c);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>类加载器将main方法所在的类，类加载，也就是读取字节码到内存。</p>
<p><strong>1.常量池载入运行时常量池</strong></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151317.png" alt="img"></p>
<p>运行时常量池只是方法区的一部分。<strong>short以内的整数是和字节码指令存放在一起，超过之后存放在常量池</strong>，32767。</p>
<p><strong>2.方法字节码载入方法区</strong></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312193745785.png" alt="image-20220312193745785"></p>
<p><strong>3. main 线程开始运行，分配栈帧内存</strong></p>
<p>（字节码文件中会显示stack=2，locals=4，4个局部变量槽位，操作数栈的深度是2，每个深度4个字节），因为有abc三个变量。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312193755210.png" alt="image-20220312193755210"></p>
<p><strong>4.执行引擎开始执行字节码</strong></p>
<p>第一个指令：<strong>bipush 10</strong></p>
<p>将一个 byte 压入操作数栈（其长度会补齐 4 个字节），类似的指令还有：sipush 将一个 short 压入操作数栈（其长度会补齐 4 个字节）；ldc 将一个 int 压入操作数栈；ldc2_w 将一个 long 压入操作数栈（分两次压入，因为 long 是 8 个字节）；这里小的数字都是和字节码指令存在一起，超过 short 范围的数字存入了常量池。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151336.png" alt="img"></p>
<p><strong>istore_1</strong></p>
<p>将操作数栈顶数据弹出，存入局部变量表的 slot 1</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151346.png" alt="img"></p>
<p><strong>ldc #3</strong></p>
<p>从常量池加载 #3 数据到操作数栈</p>
<p>Short.MAX_VALUE 是 32767，所以 32768 = Short.MAX_VALUE + 1 实际是在编译期间计算好的</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151421.png" alt="img"></p>
<p><strong>istore_2</strong></p>
<p>将操作数栈顶数据弹出，存入局部变量表的 slot 2</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151441.png" alt="img"></p>
<p><strong>iload_1、iload_2</strong></p>
<p>把局部变量读到操作栈</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151459.png" alt="img"></p>
<p><strong>iadd</strong></p>
<p>将操作数栈中的两个元素弹出栈并相加，结果在压入操作数栈中</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151523.png" alt="img"></p>
<p><strong>istore_3</strong></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151547.png" alt="img"></p>
<p><strong>getstatic #4</strong></p>
<p>在运行时常量池中找到#4，发现是一个对象，在堆内存中找到该对象，并将其<strong>引用</strong>放入操作数栈中</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151605.png" alt="img"></p>
<p><strong>iload_3</strong></p>
<p>将局部变量表中3号位置的元素压入操作数栈中</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200608151624.png" alt="img"></p>
<p><strong>invokevirtual #5</strong></p>
<ul>
<li><p>找到常量池 #5 项</p>
</li>
<li><p>定位到方法区 java/io/PrintStream.println:(I)V 方法</p>
</li>
<li><p>生成新的栈帧（分配 locals、stack等）</p>
</li>
<li><p>传递参数，执行新栈帧中的字节码</p>
</li>
<li><p>执行完毕，弹出栈帧</p>
</li>
<li><p>清除 main 操作数栈内容</p>
</li>
</ul>
<p><strong>return</strong></p>
<p>完成 main 方法调用，弹出 main 栈帧，程序结束。</p>
<p>从字节码角度分析 a++ ++a，a++ 和 ++a 的区别是先执行 iload 还是 先执行 iinc， iinc的自增操作是在局部变量的槽位内完成。</p>
<h4 id="条件判断指令"><a href="#条件判断指令" class="headerlink" title="条件判断指令"></a>条件判断指令</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo3_3</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(a == <span class="number">0</span>) &#123;</span><br><span class="line">            a = <span class="number">10</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            a = <span class="number">20</span>;</span><br><span class="line">        &#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">0: iconst_0</span><br><span class="line">1: istore_1</span><br><span class="line">2: iload_1</span><br><span class="line">3: ifne 12    //比较指令</span><br><span class="line">6: bipush 10</span><br><span class="line">8: istore_1</span><br><span class="line">9: goto 15</span><br><span class="line">12: bipush 20</span><br><span class="line">14: istore_1</span><br><span class="line">15: return</span><br></pre></td></tr></table></figure>

<p>byte，short，char 比较大小都会按 int 比较，因为操作数栈都是 4 字节</p>
<p>判断后goto 用来进行跳转到指定行号的字节码</p>
<p>long，float，double 的比较，参考 <a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lcmp">https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lcmp</a></p>
<h4 id="循环控制指令"><a href="#循环控制指令" class="headerlink" title="循环控制指令"></a>循环控制指令</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (a &lt; <span class="number">10</span>) &#123;</span><br><span class="line">        a++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="number">0</span>: iconst_0</span><br><span class="line"><span class="number">1</span>: istore_1</span><br><span class="line"><span class="number">2</span>: iload_1</span><br><span class="line"><span class="number">3</span>: bipush <span class="number">10</span></span><br><span class="line"><span class="number">5</span>: if_icmpge <span class="number">14</span></span><br><span class="line"><span class="number">8</span>: iinc <span class="number">1</span>, <span class="number">1</span></span><br><span class="line"><span class="number">11</span>: goto <span class="number">2</span></span><br><span class="line"><span class="number">14</span>: <span class="keyword">return</span></span><br></pre></td></tr></table></figure>

<p>比较 while 和 for 的字节码，是一模一样的。</p>
<h4 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h4><p><strong>1）<cinit>()v</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        i = <span class="number">20</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        i = <span class="number">30</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译器会按从上至下的顺序，<strong>收集所有 static 静态代码块和静态成员赋值的代码，合并为一个特殊的方法<cinit> ()V ：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">0: bipush 10</span><br><span class="line">2: putstatic #2 // Field i:I</span><br><span class="line">5: bipush 20</span><br><span class="line">7: putstatic #2 // Field i:I</span><br><span class="line">10: bipush 30</span><br><span class="line">12: putstatic #2 // Field i:I</span><br><span class="line">15: return</span><br></pre></td></tr></table></figure>

<p><cinit> ()V 方法会在类加载的<strong>初始化阶段被调用</strong></p>
<p>2)<init> ()V ：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo4</span> &#123;</span><br><span class="line">	<span class="keyword">private</span> <span class="type">String</span> <span class="variable">a</span> <span class="operator">=</span> <span class="string">&quot;s1&quot;</span>;</span><br><span class="line">	&#123;</span><br><span class="line">		b = <span class="number">20</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">	&#123;</span><br><span class="line">		a = <span class="string">&quot;s2&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="title function_">Demo4</span><span class="params">(String a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">		<span class="built_in">this</span>.a = a;</span><br><span class="line">		<span class="built_in">this</span>.b = b;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">Demo4</span> <span class="variable">d</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo4</span>(<span class="string">&quot;s3&quot;</span>, <span class="number">30</span>);</span><br><span class="line">		System.out.println(d.a);</span><br><span class="line">		System.out.println(d.b);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译器会按从上至下的顺序，收集所有 {} 代码块和成员变量赋值的代码，形成新的构造方法，但原始构造方法内的代码总是在最后</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">stack=2, locals=3, args_size=3</span><br><span class="line">    0: aload_0</span><br><span class="line">    1: invokespecial #1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">    4: aload_0</span><br><span class="line">    5: ldc           #2                  // String s1</span><br><span class="line">    7: putfield      #3                  // Field a:Ljava/lang/String;</span><br><span class="line">    10: aload_0</span><br><span class="line">    11: bipush        20</span><br><span class="line">    13: putfield      #4                  // Field b:I</span><br><span class="line">    16: aload_0</span><br><span class="line">    17: bipush        10</span><br><span class="line">    19: putfield      #4                  // Field b:I</span><br><span class="line">    22: aload_0</span><br><span class="line">    23: ldc           #5                  // String s2</span><br><span class="line">    25: putfield      #3                  // Field a:Ljava/lang/String;</span><br><span class="line">    //原始构造方法在最后执行</span><br><span class="line">    28: aload_0</span><br><span class="line">    29: aload_1</span><br><span class="line">    30: putfield      #3                  // Field a:Ljava/lang/String;</span><br><span class="line">    33: aload_0</span><br><span class="line">    34: iload_2</span><br><span class="line">    35: putfield      #4                  // Field b:I</span><br><span class="line">    38: return</span><br></pre></td></tr></table></figure>

<h4 id="方法调用"><a href="#方法调用" class="headerlink" title="方法调用"></a>方法调用</h4><p>看一下几种不同的方法调用对应的字节码指令</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo5</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="title function_">Demo5</span><span class="params">()</span> &#123;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">test1</span><span class="params">()</span> &#123;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">test2</span><span class="params">()</span> &#123;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test3</span><span class="params">()</span> &#123;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test4</span><span class="params">()</span> &#123;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">Demo5</span> <span class="variable">demo5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo5</span>();</span><br><span class="line">		demo5.test1();</span><br><span class="line">		demo5.test2();</span><br><span class="line">		demo5.test3();</span><br><span class="line">		Demo5.test4();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>不同方法在调用时，对应的虚拟机指令有所区别</p>
<ul>
<li>私有、构造、被final修饰的方法，在调用时都使用<strong>invokespecial</strong>指令，属于静态绑定</li>
<li>普通成员方法在调用时，使用<strong>invokevirtual</strong>指令。因为编译期间无法确定该方法的内容，只有在运行期间才能确定，属于动态绑定。</li>
<li>静态方法在调用时使用invokestatic指令</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Code:</span><br><span class="line">stack=2, locals=2, args_size=1</span><br><span class="line">0: new           #2                  // class com/nyima/JVM/day5/Demo5 </span><br><span class="line">3: dup</span><br><span class="line">4: invokespecial #3                  // Method &quot;&lt;init&gt;&quot;:()V</span><br><span class="line">7: astore_1</span><br><span class="line">8: aload_1</span><br><span class="line">9: invokespecial #4                  // Method test1:()V</span><br><span class="line">12: aload_1</span><br><span class="line">13: invokespecial #5                  // Method test2:()V</span><br><span class="line">16: aload_1</span><br><span class="line">17： pop</span><br><span class="line">18: invokevirtual #6                  // Method test3:()V</span><br><span class="line">20: invokestatic  #7                  // Method test4:()V</span><br><span class="line">23: return</span><br></pre></td></tr></table></figure>

<ul>
<li>new 是创建【对象】，给对象分配堆内存，执行成功会将【对象引用】压入操作数栈</li>
<li>dup 将栈顶的内容复制一份。为什么需要两份引用呢，一个是要配合 invokespecial 调用该对象的构造方法 “init”:()V （会消耗掉栈顶一个引用），另一个要配合astore_1 赋值给局部变量。</li>
<li>成员方法与静态方法调用的另一个区别是，执行方法前是否需要【对象引用】</li>
<li>比较有意思的是 d.test4(); 是通过【对象引用】调用一个静态方法，可以看到在调用 invokestatic 之前执行了 <strong>pop</strong> 指令，把【对象引用】从操作数栈弹掉了，静态方法不需要通过对象调用。</li>
<li>还有一个执行 invokespecial 的情况是通过 super 调用父类方法。</li>
</ul>
<h4 id="多态原理"><a href="#多态原理" class="headerlink" title="多态原理"></a>多态原理</h4><p>因为普通成员方法需要在运行时才能确定具体的内容，所以虚拟机需要调用<strong>invokevirtual</strong>指令</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 演示多态原理，注意加上下面的 JVM 参数，禁用指针压缩</span></span><br><span class="line"><span class="comment">* -XX:-UseCompressedOops -XX:-UseCompressedClassPointers</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo3_10</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(Animal animal)</span> &#123;</span><br><span class="line">        animal.eat();</span><br><span class="line">        System.out.println(animal.toString());</span><br><span class="line">&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    	test(<span class="keyword">new</span> <span class="title class_">Cat</span>());</span><br><span class="line">    	test(<span class="keyword">new</span> <span class="title class_">Dog</span>());</span><br><span class="line">    	System.in.read();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span>;</span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="string">&quot;我是&quot;</span> + <span class="built_in">this</span>.getClass().getSimpleName();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">	System.out.println(<span class="string">&quot;啃骨头&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> &#123;</span><br><span class="line">	System.out.println(<span class="string">&quot;吃鱼&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>1）运行代码，停在 System.in.read() 方法上，这时运行 jps 获取进程 id</p>
<p>2）运行 HSDB 工具，进入 JDK 安装目录，执行java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB，进入图形界面 attach 进程 id</p>
<p>3）查找某个对象：打开 Tools -&gt; Find Object By Query</p>
<p>输入 select d from cn.itcast.jvm.t3.bytecode.Dog d 点击 Execute 执行</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194253723.png" alt="image-20220312194253723"></p>
<p>4）查看对象内存结构。点击超链接可以看到对象的内存结构，此对象没有任何属性，因此只有对象头的 16 字节，前 8 字节是 MarkWord，后 8 字节就是对象的 Class 指针。但目前看不到它的实际地址。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194356452.png" alt="image-20220312194356452"></p>
<p>5）查看对象 Class 的内存地址</p>
<p>可以通过 Windows -&gt; Console 进入命令行模式，执行</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mem 0x00000001299b4978 2</span><br></pre></td></tr></table></figure>

<p>mem 有两个参数，参数 2 是查看 2 行（即 16 字节，因此只有对象头的 16 字节，前 8 字节是 MarkWord，后 8 字节就是对象的 Class 指针）</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194407461.png" alt="image-20220312194407461"></p>
<p>结果中第二行 0x000000001b7d4028 即为 Class 的内存地址</p>
<p>6）查看类的 vtable</p>
<p>方法1：tools 进入 Inspector 工具，输入刚才的 Class 内存地址，看到如下界面</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312194443799.png" alt="image-20220312194443799"></p>
<p>都可以找到 Dog Class 的 vtable 长度为 6，意思就是 Dog 类有 6 个虚方法（多态相关的，final、static 不会列入）</p>
<p>从 Class 的起始地址开始算，偏移 0x1b8 就是 vtable 的起始地址，进行计算得到：0x000000001b7d41e0</p>
<p>通过 Windows -&gt; Console 进入命令行模式，执行</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mem 0x000000001b7d41e0 6</span><br><span class="line">0x000000001b7d41e0: 0x000000001b3d1b10</span><br><span class="line">0x000000001b7d41e8: 0x000000001b3d15e8</span><br><span class="line">0x000000001b7d41f0: 0x000000001b7d35e8</span><br><span class="line">0x000000001b7d41f8: 0x000000001b3d1540</span><br><span class="line">0x000000001b7d4200: 0x000000001b3d1678</span><br><span class="line">0x000000001b7d4208: 0x000000001b7d3fa8</span><br></pre></td></tr></table></figure>

<p>就得到了 6 个虚方法的入口地址</p>
<p>7）验证方法地址</p>
<p>通过 Tools -&gt; Class Browser 查看每个类的方法定义，比较可知</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Dog - public void eat() @0x000000001b7d3fa8</span><br><span class="line">Animal - public java.lang.String toString() @0x000000001b7d35e8;</span><br><span class="line">Object - protected void finalize() @0x000000001b3d1b10;</span><br><span class="line">Object - public boolean equals(java.lang.Object) @0x000000001b3d15e8;</span><br><span class="line">Object - public native int hashCode() @0x000000001b3d1540;</span><br><span class="line">Object - protected native java.lang.Object clone() @0x000000001b3d1678;</span><br></pre></td></tr></table></figure>

<p>对号入座</p>
<ul>
<li>发现 eat() 方法是 Dog 类自己的</li>
<li>toString() 方法是继承 String 类的 </li>
<li>finalize() ,equals()，hashCode()，clone() 都是继承 Object 类的</li>
</ul>
<p><strong>小结：</strong></p>
<p>在执行invokevirtual指令时，经历了以下几个步骤</p>
<ul>
<li>先通过栈帧中对象的引用找到对象</li>
<li>分析对象头，找到对象实际的Class</li>
<li>Class结构中有<strong>vtable</strong></li>
<li>查询vtable找到方法的具体地址</li>
<li>执行方法的字节码</li>
</ul>
<h4 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h4><p><strong>try-catch</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo1</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			i = <span class="number">10</span>;</span><br><span class="line">		&#125;<span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			i = <span class="number">20</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>下面的字节码省略了不重要的部分</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">stack=1, locals=3, args_size=1</span><br><span class="line">    0: iconst_0</span><br><span class="line">    1: istore_1</span><br><span class="line">    2: bipush 10</span><br><span class="line">    4: istore_1</span><br><span class="line">    5: goto 12</span><br><span class="line">    8: astore_2</span><br><span class="line">    9: bipush 20</span><br><span class="line">    11: istore_1</span><br><span class="line">    12: return</span><br><span class="line">Exception table:  //多出来一个异常表 </span><br><span class="line">	from to target type</span><br><span class="line">		2 5 8 Class java/lang/Exception</span><br><span class="line">LineNumberTable: ...</span><br><span class="line">LocalVariableTable:</span><br><span class="line">	Start Length Slot Name Signature</span><br><span class="line">		9 3 2 e Ljava/lang/Exception;</span><br><span class="line">		0 13 0 args [Ljava/lang/String;</span><br><span class="line">		2 11 1 i I</span><br><span class="line">StackMapTable: ...</span><br><span class="line">MethodParameters: ...</span><br></pre></td></tr></table></figure>

<ul>
<li>可以看到多出来一个 Exception table 的结构，[from, to) 是<strong>前闭后开</strong>（也就是检测2~4行）的检测范围，一旦这个范围内的字节码执行出现异常，则通过 type 匹配异常类型，如果一致，进入 target 所指示行号</li>
<li>8行的字节码指令 astore_2 是将异常对象引用存入局部变量表的2号位置（为e）</li>
</ul>
<p><strong>多个single-catch</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo1</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			i = <span class="number">10</span>;</span><br><span class="line">		&#125;<span class="keyword">catch</span> (ArithmeticException e) &#123;</span><br><span class="line">			i = <span class="number">20</span>;</span><br><span class="line">		&#125;<span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			i = <span class="number">30</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对应的字节码</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Code:</span><br><span class="line">     stack=1, locals=3, args_size=1</span><br><span class="line">        0: iconst_0</span><br><span class="line">        1: istore_1</span><br><span class="line">        2: bipush        10</span><br><span class="line">        4: istore_1</span><br><span class="line">        5: goto          19</span><br><span class="line">        8: astore_2</span><br><span class="line">        9: bipush        20</span><br><span class="line">       11: istore_1</span><br><span class="line">       12: goto          19</span><br><span class="line">       15: astore_2</span><br><span class="line">       16: bipush        30</span><br><span class="line">       18: istore_1</span><br><span class="line">       19: return</span><br><span class="line">     Exception table:</span><br><span class="line">        from    to  target type</span><br><span class="line">            2     5     8   Class java/lang/ArithmeticException</span><br><span class="line">            2     5    15   Class java/lang/Exception</span><br></pre></td></tr></table></figure>

<p>因为异常出现时，只能进入 Exception table 中一个分支，所以局部变量表 slot 2 位置被共用</p>
<p><strong>multi-catch 的情况</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo3_11_3</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="type">Method</span> <span class="variable">test</span> <span class="operator">=</span> Demo3_11_3.class.getMethod(<span class="string">&quot;test&quot;</span>);</span><br><span class="line">			test.invoke(<span class="literal">null</span>);</span><br><span class="line">		&#125; <span class="keyword">catch</span> (NoSuchMethodException | IllegalAccessException 		|InvocationTargetException e) &#123;</span><br><span class="line">		e.printStackTrace();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">stack=3, locals=2, args_size=1</span><br><span class="line">0: ldc #2</span><br><span class="line">2: ldc #3</span><br><span class="line">4: iconst_0</span><br><span class="line">5: anewarray #4</span><br><span class="line">8: invokevirtual #5</span><br><span class="line">11: astore_1</span><br><span class="line">12: aload_1</span><br><span class="line">13: aconst_null</span><br><span class="line">14: iconst_0</span><br><span class="line">15: anewarray #6</span><br><span class="line">18: invokevirtual #7</span><br><span class="line">21: pop</span><br><span class="line">22: goto 30</span><br><span class="line">25: astore_1</span><br><span class="line">26: aload_1</span><br><span class="line">27: invokevirtual #11 // e.printStackTrace:()V</span><br><span class="line">30: return</span><br><span class="line">Exception table:</span><br><span class="line">from to target type</span><br><span class="line">0 22 25 Class java/lang/NoSuchMethodException</span><br><span class="line">0 22 25 Class java/lang/IllegalAccessException</span><br><span class="line">0 22 25 Class java/lang/reflect/InvocationTargetException</span><br><span class="line">LineNumberTable: ...</span><br><span class="line">LocalVariableTable:</span><br><span class="line">Start Length Slot Name Signature</span><br><span class="line">12 10 1 test Ljava/lang/reflect/Method;</span><br><span class="line">26 4 1 e Ljava/lang/ReflectiveOperationException;</span><br><span class="line">0 31 0 args [Ljava/lang/String;</span><br><span class="line">StackMapTable: ...</span><br><span class="line">MethodParameters: ...</span><br></pre></td></tr></table></figure>

<p>上面的不管符合哪一种异常，都进入target 25</p>
<p><strong>finally</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public class Demo2 &#123;</span><br><span class="line">	public static void main(String[] args) &#123;</span><br><span class="line">		int i = 0;</span><br><span class="line">		try &#123;</span><br><span class="line">			i = 10;</span><br><span class="line">		&#125; catch (Exception e) &#123;</span><br><span class="line">			i = 20;</span><br><span class="line">		&#125; finally &#123;</span><br><span class="line">			i = 30;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">Code:</span><br><span class="line">     stack=1, locals=4, args_size=1</span><br><span class="line">        0: iconst_0</span><br><span class="line">        1: istore_1</span><br><span class="line">        //try块</span><br><span class="line">        2: bipush        10</span><br><span class="line">        4: istore_1</span><br><span class="line">        //try块执行完后，会执行finally    </span><br><span class="line">        5: bipush        30</span><br><span class="line">        7: istore_1</span><br><span class="line">        8: goto          27</span><br><span class="line">       //catch块     </span><br><span class="line">       11: astore_2 //异常信息放入局部变量表的2号槽位</span><br><span class="line">       12: bipush        20</span><br><span class="line">       14: istore_1</span><br><span class="line">       //catch块执行完后，会执行finally        </span><br><span class="line">       15: bipush        30</span><br><span class="line">       17: istore_1</span><br><span class="line">       18: goto          27</span><br><span class="line">       //出现异常，但未被Exception捕获，会抛出其他异常，这时也需要执行finally块中的代码   </span><br><span class="line">       21: astore_3</span><br><span class="line">       22: bipush        30</span><br><span class="line">       24: istore_1</span><br><span class="line">       25: aload_3</span><br><span class="line">       26: athrow  //抛出异常</span><br><span class="line">       27: return</span><br><span class="line">     Exception table:</span><br><span class="line">        from    to  target type</span><br><span class="line">            2     5    11   Class java/lang/Exception</span><br><span class="line">            2     5    21   any</span><br><span class="line">           11    15    21   any</span><br></pre></td></tr></table></figure>

<p>可以看到 ﬁnally 中的代码被<strong>复制了 3 份</strong>，分别放入 try 流程，catch 流程以及 catch剩余的异常类型流程。</p>
<p>虽然从字节码指令看来，每个块中都有finally块，但是finally块中的代码<strong>只会被执行一次</strong></p>
<p><strong>finally中的return</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo3</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> Demo3.test();</span><br><span class="line">         <span class="comment">//结果为20</span></span><br><span class="line">		System.out.println(i);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="type">int</span> i;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			i = <span class="number">10</span>;</span><br><span class="line">			<span class="keyword">return</span> i;</span><br><span class="line">		&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">			i = <span class="number">20</span>;</span><br><span class="line">			<span class="keyword">return</span> i;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">stack=1, locals=2, args_size=0</span><br><span class="line">0: bipush 10 // &lt;- 10 放入栈顶</span><br><span class="line">2: istore_0 // 10 -&gt; slot 0 (从栈顶移除了)</span><br><span class="line">3: bipush 20 // &lt;- 20 放入栈顶</span><br><span class="line">5: ireturn // 返回栈顶 int(20)</span><br><span class="line">6: astore_1 // catch any -&gt; slot 1</span><br><span class="line">7: bipush 20 // &lt;- 20 放入栈顶</span><br><span class="line">9: ireturn // 返回栈顶 int(20)</span><br><span class="line">Exception table:</span><br><span class="line">from to target type</span><br><span class="line">0 3 6 any</span><br><span class="line">LineNumberTable: ...</span><br><span class="line">StackMapTable: ...</span><br></pre></td></tr></table></figure>

<ul>
<li>由于 ﬁnally 中的 <strong>ireturn</strong> 被插入了所有可能的流程，因此返回结果肯定以ﬁnally的为准</li>
<li>至于字节码中第 2 行，只是暂存了返回值</li>
<li>跟上例中的 ﬁnally 相比，发现<strong>没有 athrow 了</strong>，这告诉我们：如果在 ﬁnally 中出现了 return，会<strong>吞掉异常</strong></li>
<li>所以<strong>不要在finally中进行返回操作</strong></li>
</ul>
<p><strong>finally不带return</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo4</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> Demo4.test();</span><br><span class="line">		System.out.println(i);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="keyword">return</span> i;</span><br><span class="line">		&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">			i = <span class="number">20</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Code:</span><br><span class="line">     stack=1, locals=3, args_size=0</span><br><span class="line">        0: bipush        10</span><br><span class="line">        2: istore_0 //赋值给i 10</span><br><span class="line">        3: iload_0	//加载到操作数栈顶</span><br><span class="line">        4: istore_1 //加载到局部变量表的1号位置</span><br><span class="line">        5: bipush        20</span><br><span class="line">        7: istore_0 //赋值给i 20</span><br><span class="line">        8: iload_1 //加载局部变量表1号位置的数10到操作数栈</span><br><span class="line">        9: ireturn //返回操作数栈顶元素 10</span><br><span class="line">       10: astore_2</span><br><span class="line">       11: bipush        20</span><br><span class="line">       13: istore_0</span><br><span class="line">       14: aload_2 //加载异常</span><br><span class="line">       15: athrow //抛出异常</span><br><span class="line">     Exception table:</span><br><span class="line">        from    to  target type</span><br><span class="line">            3     5    10   any</span><br></pre></td></tr></table></figure>

<h4 id="Synchronized"><a href="#Synchronized" class="headerlink" title="Synchronized"></a>Synchronized</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo5</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">		<span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Lock</span>();</span><br><span class="line">		<span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">			System.out.println(i);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Lock</span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<p>对应字节码</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">stack=2, locals=4, args_size=1</span><br><span class="line">    0: new #2 // new Object</span><br><span class="line">    3: dup</span><br><span class="line">    4: invokespecial #1 // invokespecial &lt;init&gt;:()V 初始化</span><br><span class="line">    7: astore_1 // lock引用放入变量槽</span><br><span class="line">    8: aload_1 // &lt;- lock （synchronized开始）</span><br><span class="line">    9: dup //lock复制两份，加锁和解锁用</span><br><span class="line">    10: astore_2 // lock引用 -&gt; slot 2</span><br><span class="line">    11: monitorenter // monitorenter(lock引用)</span><br><span class="line">    12: getstatic #3 // &lt;- System.out</span><br><span class="line">    15: ldc #4 // &lt;- &quot;ok&quot;</span><br><span class="line">    17: invokevirtual #5 // invokevirtual println:</span><br><span class="line">    (Ljava/lang/String;)V</span><br><span class="line">    20: aload_2 // &lt;- slot 2(lock引用)</span><br><span class="line">    21: monitorexit // monitorexit(lock引用)</span><br><span class="line">    22: goto 30</span><br><span class="line">    25: astore_3 // any -&gt; slot 3  出现异常时，也会解锁</span><br><span class="line">    26: aload_2 // &lt;- slot 2(lock引用)</span><br><span class="line">    27: monitorexit // monitorexit(lock引用)</span><br><span class="line">    28: aload_3</span><br><span class="line">    29: athrow</span><br><span class="line">    30: return</span><br><span class="line">Exception table:</span><br><span class="line">	from to target type</span><br><span class="line">		12 22 25 any</span><br><span class="line">		25 28 25 any</span><br><span class="line">LineNumberTable: ...</span><br><span class="line">LocalVariableTable:</span><br><span class="line">Start Length Slot Name Signature</span><br><span class="line">0 31 0 args [Ljava/lang/String;</span><br><span class="line">8 23 1 lock Ljava/lang/Object;</span><br><span class="line">StackMapTable: ...</span><br><span class="line">MethodParameters: ...</span><br></pre></td></tr></table></figure>

<ul>
<li><p>可以看出，无论何时出现异常，都会跳转到27行，将异常放入局部变量中，并进行解锁操作，然后加载异常并抛出异常。</p>
</li>
<li><p>注意：方法级别的 synchronized 不会在字节码指令中有所体现</p>
</li>
</ul>
<h3 id="编译期处理"><a href="#编译期处理" class="headerlink" title="编译期处理"></a>编译期处理</h3><p>所谓的<strong>语法糖</strong> ，其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中，自动生成和转换的一些代码，主要是为了减轻程序员的负担，算是 java 编译器给我们的一个额外福利（给糖吃嘛）</p>
<p><strong>注意</strong>，以下代码的分析，借助了 javap 工具，idea 的反编译功能，idea 插件 jclasslib 等工具，只是伪代码，并不是编译器还会转换出中间的 java 源码。</p>
<h4 id="默认构造函数"><a href="#默认构造函数" class="headerlink" title="默认构造函数"></a>默认构造函数</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy1</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>经过编译期优化后</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy1</span> &#123;</span><br><span class="line">   <span class="comment">//这个无参构造器是java编译器帮我们加上的</span></span><br><span class="line">   <span class="keyword">public</span> <span class="title function_">Candy1</span><span class="params">()</span> &#123;</span><br><span class="line">      <span class="comment">//即调用父类 Object 的无参构造方法，即调用 java/lang/Object.&quot; &lt;init&gt;&quot;:()V</span></span><br><span class="line">      <span class="built_in">super</span>();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="自动拆装箱"><a href="#自动拆装箱" class="headerlink" title="自动拆装箱"></a>自动拆装箱</h4><p>基本类型和其包装类型的相互转换过程，称为拆装箱.在JDK 5以后，它们的转换可以在编译期自动完成</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy2</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="type">Integer</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">      <span class="type">int</span> <span class="variable">y</span> <span class="operator">=</span> x;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>转换过程如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy2</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="comment">//基本类型赋值给包装类型，称为装箱</span></span><br><span class="line">      <span class="type">Integer</span> <span class="variable">x</span> <span class="operator">=</span> Integer.valueOf(<span class="number">1</span>);</span><br><span class="line">      <span class="comment">//包装类型赋值给基本类型，称谓拆箱</span></span><br><span class="line">      <span class="type">int</span> <span class="variable">y</span> <span class="operator">=</span> x.intValue();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="泛型集合取值"><a href="#泛型集合取值" class="headerlink" title="泛型集合取值"></a>泛型集合取值</h4><p>泛型也是在 JDK 5 开始加入的特性，但 java 在编译泛型代码后会执行 泛型擦除 的动作，即泛型信息在编译为字节码之后就丢失了，实际的类型都当做了 Object 类型来处理：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy3</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">      list.add(<span class="number">10</span>);<span class="comment">// 实际调用的是 List.add(Object e)</span></span><br><span class="line">      <span class="type">Integer</span> <span class="variable">x</span> <span class="operator">=</span> list.get(<span class="number">0</span>);<span class="comment">// 需要将 Object 转为 Integer Integer x = (Integer)list.get(0)</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对应字节码</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">Code:</span><br><span class="line">    stack=2, locals=3, args_size=1</span><br><span class="line">       0: new           #2                  // class java/util/ArrayList</span><br><span class="line">       3: dup</span><br><span class="line">       4: invokespecial #3                  // Method java/util/ArrayList.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line">       7: astore_1</span><br><span class="line">       8: aload_1</span><br><span class="line">       9: bipush        10</span><br><span class="line">      11: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;</span><br><span class="line">      //这里进行了泛型擦除，实际调用的是add(Objcet o)</span><br><span class="line">      14: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z</span><br><span class="line">      19: pop</span><br><span class="line">      20: aload_1</span><br><span class="line">      21: iconst_0</span><br><span class="line">      //这里也进行了泛型擦除，实际调用的是get(Object o)   </span><br><span class="line">      22: invokeinterface #6,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;</span><br><span class="line">//这里进行了类型转换，将Object转换成了Integer</span><br><span class="line">      27: checkcast     #7                  // class java/lang/Integer</span><br><span class="line">      30: astore_2</span><br><span class="line">      31: return</span><br><span class="line">    LineNumberTable:</span><br><span class="line">        line 8: 0</span><br><span class="line">        line 9: 8</span><br><span class="line">        line 10: 20</span><br><span class="line">        line 11: 31</span><br><span class="line">	LocalVariableTable:</span><br><span class="line">		Start Length Slot Name Signature</span><br><span class="line">		0 32 0 args [Ljava/lang/String;</span><br><span class="line">		8 24 1 list Ljava/util/List;</span><br><span class="line">	LocalVariableTypeTable:</span><br><span class="line">		Start Length Slot Name Signature</span><br><span class="line">		8 24 1 list Ljava/util/List&lt;Ljava/lang/Integer;&gt;;</span><br></pre></td></tr></table></figure>

<p>擦除的是字节码上的泛型信息，可以看到 LocalVariableTypeTable 仍然保留了方法参数泛型的信息。</p>
<p>使用反射，仍然能够获得这些信息：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Set&lt;Integer&gt; <span class="title function_">test</span><span class="params">(List&lt;String&gt; list, Map&lt;Integer, Object&gt; map)</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Method</span> <span class="variable">test</span> <span class="operator">=</span> Candy3.class.getMethod(<span class="string">&quot;test&quot;</span>, List.class, Map.class);</span><br><span class="line">Type[] types = test.getGenericParameterTypes();</span><br><span class="line"><span class="keyword">for</span> (Type type : types) &#123;</span><br><span class="line">	<span class="keyword">if</span> (type <span class="keyword">instanceof</span> ParameterizedType) &#123;</span><br><span class="line">		<span class="type">ParameterizedType</span> <span class="variable">parameterizedType</span> <span class="operator">=</span> (ParameterizedType) type;</span><br><span class="line">		System.out.println(<span class="string">&quot;原始类型 - &quot;</span> + 	parameterizedType.getRawType());</span><br><span class="line">		Type[] arguments = parameterizedType.getActualTypeArguments();</span><br><span class="line">		<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; arguments.length; i++) &#123;</span><br><span class="line">			System.out.printf(<span class="string">&quot;泛型参数[%d] - %s\n&quot;</span>, i, arguments[i]);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//输出</span></span><br><span class="line"><span class="comment">//原始类型 - interface java.util.List</span></span><br><span class="line"><span class="comment">//泛型参数[0] - class java.lang.String</span></span><br><span class="line"><span class="comment">//原始类型 - interface java.util.Map</span></span><br><span class="line"><span class="comment">//泛型参数[0] - class java.lang.Integer</span></span><br><span class="line"><span class="comment">//泛型参数[1] - class java.lang.Object</span></span><br></pre></td></tr></table></figure>

<h4 id="可变参数"><a href="#可变参数" class="headerlink" title="可变参数"></a>可变参数</h4><p>可变参数也是 JDK 5 开始加入的新特性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy4</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">foo</span><span class="params">(String... args)</span> &#123;</span><br><span class="line">      <span class="comment">//将args赋值给arr</span></span><br><span class="line">      String[] arr = args;</span><br><span class="line">      System.out.println(arr.length);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      foo(<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;world&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可变参数 String… args 其实是一个 String[] args ，从代码中的赋值语句中就可以看出来。 同样 java 编译器会在编译期间将上述代码变换为：</p>
<p><strong>注意</strong>：如果调用了 foo() 则等价代码为 foo(new String[]{}) ，<strong>创建了一个空的数组，而不会传递null 进去</strong></p>
<h4 id="foreach"><a href="#foreach" class="headerlink" title="foreach"></a>foreach</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy5</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//数组赋初值的简化写法也是一种语法糖。</span></span><br><span class="line">		<span class="type">int</span>[] arr = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">		<span class="keyword">for</span>(<span class="type">int</span> x : arr) &#123;</span><br><span class="line">			System.out.println(x);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译器会帮我们转换为</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy5</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> Candy5 &#123;&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">int</span>[] arr = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">		<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;arr.length; ++i) &#123;</span><br><span class="line">			<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> arr[i];</span><br><span class="line">			System.out.println(x);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果是集合使用foreach</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy5</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      List&lt;Integer&gt; list = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>);</span><br><span class="line">      <span class="keyword">for</span> (Integer x : list) &#123;</span><br><span class="line">         System.out.println(x);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>需要该集合类实现了Iterable接口</strong>，因为集合的遍历需要用到迭代器Iterator</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy5</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> Candy5 &#123;&#125;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      List&lt;Integer&gt; list = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>);</span><br><span class="line">      <span class="comment">//获得该集合的迭代器</span></span><br><span class="line">      Iterator&lt;Integer&gt; iterator = list.iterator();</span><br><span class="line">      <span class="keyword">while</span>(iterator.hasNext()) &#123;</span><br><span class="line">         <span class="type">Integer</span> <span class="variable">x</span> <span class="operator">=</span> iterator.next();</span><br><span class="line">         System.out.println(x);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="switch字符串"><a href="#switch字符串" class="headerlink" title="switch字符串"></a>switch字符串</h4><p>从 JDK7 开始，switch 可以作用于字符串和枚举类，这个功能其实也是语法糖，例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy6</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">      <span class="keyword">switch</span> (str) &#123;</span><br><span class="line">         <span class="keyword">case</span> <span class="string">&quot;hello&quot;</span> :</span><br><span class="line">            System.out.println(<span class="string">&quot;h&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">case</span> <span class="string">&quot;world&quot;</span> :</span><br><span class="line">            System.out.println(<span class="string">&quot;w&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>会被编译器转换为：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy6</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="title function_">Candy6</span><span class="params">()</span> &#123;   </span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">      <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">      <span class="comment">//通过字符串的hashCode+value来判断是否匹配</span></span><br><span class="line">      <span class="keyword">switch</span> (str.hashCode()) &#123;</span><br><span class="line">         <span class="comment">//hello的hashCode</span></span><br><span class="line">         <span class="keyword">case</span> <span class="number">99162322</span> :</span><br><span class="line">            <span class="comment">//再次比较，因为字符串的hashCode有可能相等</span></span><br><span class="line">            <span class="keyword">if</span>(str.equals(<span class="string">&quot;hello&quot;</span>)) &#123;</span><br><span class="line">               x = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="comment">//world的hashCode</span></span><br><span class="line">         <span class="keyword">case</span> <span class="number">11331880</span> :</span><br><span class="line">            <span class="keyword">if</span>(str.equals(<span class="string">&quot;world&quot;</span>)) &#123;</span><br><span class="line">               x = <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">//用第二个switch在进行输出判断</span></span><br><span class="line">      <span class="keyword">switch</span> (x) &#123;</span><br><span class="line">         <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">            System.out.println(<span class="string">&quot;h&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">            System.out.println(<span class="string">&quot;w&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>过程说明：</p>
<ul>
<li>在编译期间，单个的switch被分为了两个<ul>
<li>第一个用来匹配字符串，并给x赋值<ul>
<li>字符串的匹配用到了字符串的hashCode，还用到了equals方法</li>
<li>使用hashCode是为了提高比较效率，使用equals是防止有hashCode冲突（如BM和C.）</li>
</ul>
</li>
<li>第二个用来根据x的值来决定输出语句</li>
</ul>
</li>
</ul>
<p><strong>注意</strong>：switch 配合String 和枚举使用时，变量不能为null</p>
<h4 id="switch枚举"><a href="#switch枚举" class="headerlink" title="switch枚举"></a>switch枚举</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy7</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="type">SEX</span> <span class="variable">sex</span> <span class="operator">=</span> SEX.MALE;</span><br><span class="line">      <span class="keyword">switch</span> (sex) &#123;</span><br><span class="line">         <span class="keyword">case</span> MALE:</span><br><span class="line">            System.out.println(<span class="string">&quot;man&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">case</span> FEMALE:</span><br><span class="line">            System.out.println(<span class="string">&quot;woman&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">SEX</span> &#123;</span><br><span class="line">   MALE, FEMALE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>转换后代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy7</span> &#123;</span><br><span class="line">   <span class="comment">/**     </span></span><br><span class="line"><span class="comment">    * 定义一个合成类（静态内部类，仅 jvm 使用，对我们不可见）     </span></span><br><span class="line"><span class="comment">    * 用来映射枚举的 ordinal 与数组元素的关系     </span></span><br><span class="line"><span class="comment">    * 枚举的 ordinal 表示枚举对象的序号，从 0 开始     </span></span><br><span class="line"><span class="comment">    * 即 MALE 的 ordinal()=0，FEMALE 的 ordinal()=1     </span></span><br><span class="line"><span class="comment">    */</span> </span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">$MAP</span> &#123;</span><br><span class="line">      <span class="comment">//数组大小即为枚举元素个数，里面存放了case用于比较的数字</span></span><br><span class="line">      <span class="keyword">static</span> <span class="type">int</span>[] map = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">2</span>];</span><br><span class="line">      <span class="keyword">static</span> &#123;</span><br><span class="line">         <span class="comment">//ordinal即枚举元素对应所在的位置，MALE为0，FEMALE为1</span></span><br><span class="line">         map[SEX.MALE.ordinal()] = <span class="number">1</span>;</span><br><span class="line">         map[SEX.FEMALE.ordinal()] = <span class="number">2</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="type">SEX</span> <span class="variable">sex</span> <span class="operator">=</span> SEX.MALE;</span><br><span class="line">      <span class="comment">//将对应位置枚举元素的值赋给x，用于case操作</span></span><br><span class="line">      <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> $MAP.map[sex.ordinal()];</span><br><span class="line">      <span class="keyword">switch</span> (x) &#123;</span><br><span class="line">         <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">            System.out.println(<span class="string">&quot;man&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">            System.out.println(<span class="string">&quot;woman&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="枚举类"><a href="#枚举类" class="headerlink" title="枚举类"></a>枚举类</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">SEX</span> &#123;</span><br><span class="line">   MALE, FEMALE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>转换后的代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Sex</span> <span class="keyword">extends</span> <span class="title class_">Enum</span>&lt;Sex&gt; &#123;   </span><br><span class="line">   <span class="comment">//静态成员常量，对应枚举类中的元素</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Sex MALE;    </span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Sex FEMALE;    </span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Sex[] $VALUES; </span><br><span class="line">    <span class="comment">//静态代码块赋初值</span></span><br><span class="line">   <span class="keyword">static</span> &#123;       </span><br><span class="line">    	MALE = <span class="keyword">new</span> <span class="title class_">Sex</span>(<span class="string">&quot;MALE&quot;</span>, <span class="number">0</span>); <span class="comment">//调用构造函数</span></span><br><span class="line">        $VALUES = <span class="keyword">new</span> <span class="title class_">Sex</span>[]&#123;MALE, FEMALE&#125;; </span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//调用父类中的方法</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Sex</span><span class="params">(String name, <span class="type">int</span> ordinal)</span> &#123;     </span><br><span class="line">        <span class="built_in">super</span>(name, ordinal);    </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Sex[] values() &#123;  </span><br><span class="line">        <span class="keyword">return</span> $VALUES.clone();  </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Sex <span class="title function_">valueOf</span><span class="params">(String name)</span> &#123; </span><br><span class="line">        <span class="keyword">return</span> Enum.valueOf(Sex.class, name);  </span><br><span class="line">    &#125;   </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="try-with-resources"><a href="#try-with-resources" class="headerlink" title="try-with-resources"></a>try-with-resources</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>(资源变量 = 创建资源对象)&#123;</span><br><span class="line">&#125; <span class="keyword">catch</span>( ) &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中资源对象需要实现 AutoCloseable 接口，例如 InputStream 、 OutputStream 、 Connection 、 Statement 、 ResultSet 等接口都实现了 AutoCloseable ，使用 try-with resources 可以不用写 finally 语句块，编译器会帮助生成关闭资源代码，例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy9</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span>(<span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;d:\\1.txt&quot;</span>)) &#123;</span><br><span class="line">        	System.out.println(is);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">        	e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>会被转换为：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy9</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Candy9</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    	<span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">&quot;d:\\1.txt&quot;</span>);</span><br><span class="line">    		<span class="type">Throwable</span> <span class="variable">t</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    		<span class="keyword">try</span> &#123;</span><br><span class="line">    			System.out.println(is);</span><br><span class="line">    		&#125; <span class="keyword">catch</span> (Throwable e1) &#123;</span><br><span class="line">    			<span class="comment">// t 是我们代码出现的异常</span></span><br><span class="line">    			t = e1;</span><br><span class="line">        		<span class="keyword">throw</span> e1;</span><br><span class="line">    		&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    			<span class="comment">// 判断了资源不为空</span></span><br><span class="line">    			<span class="keyword">if</span> (is != <span class="literal">null</span>) &#123;</span><br><span class="line">    				<span class="comment">// 如果我们代码有异常</span></span><br><span class="line">    				<span class="keyword">if</span> (t != <span class="literal">null</span>) &#123;</span><br><span class="line">    					<span class="keyword">try</span> &#123;</span><br><span class="line">    						is.close();</span><br><span class="line">    					&#125; <span class="keyword">catch</span> (Throwable e2) &#123;</span><br><span class="line">    						<span class="comment">// 如果 close 出现异常，作为被压制异常添加</span></span><br><span class="line">    						t.addSuppressed(e2);</span><br><span class="line">    					&#125;</span><br><span class="line">    				&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  						<span class="comment">// 如果我们代码没有异常，close 出现的异常就是最后 catch 块中的 e</span></span><br><span class="line">    					is.close();</span><br><span class="line">    					&#125;</span><br><span class="line">    				&#125;</span><br><span class="line">    			&#125;</span><br><span class="line">        	&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">    			e.printStackTrace();</span><br><span class="line">    		&#125;</span><br><span class="line">    	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>设计一个 addSuppressed(Throwable e) （添加被压制异常）是为了防止异常信息的丢失：代码执行和关闭资源都有可能出现异常。</p>
<p>举例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test6</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="keyword">try</span> (<span class="type">MyResource</span> <span class="variable">resource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyResource</span>()) &#123;</span><br><span class="line">			<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>/<span class="number">0</span>;</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">			e.printStackTrace();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyResource</span> <span class="keyword">implements</span> <span class="title class_">AutoCloseable</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;close 异常&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>输出：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">java.lang.ArithmeticException: / by zero</span><br><span class="line">	at test.Test6.main(Test6.java:<span class="number">7</span>)</span><br><span class="line">	Suppressed: java.lang.Exception: close 异常</span><br><span class="line">		at test.MyResource.close(Test6.java:<span class="number">18</span>)</span><br><span class="line">		at test.Test6.main(Test6.java:<span class="number">6</span>)</span><br></pre></td></tr></table></figure>

<p>如以上代码所示，两个异常信息都不会丢。</p>
<h4 id="方法重写时的桥接方法"><a href="#方法重写时的桥接方法" class="headerlink" title="方法重写时的桥接方法"></a>方法重写时的桥接方法</h4><p>方法重写时对返回值分两种情况：</p>
<ul>
<li><p>父子类的返回值完全一致</p>
</li>
<li><p>子类返回值可以是父类返回值的子类</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> Number <span class="title function_">m</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> <span class="keyword">extends</span> <span class="title class_">A</span> &#123;</span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="comment">// 子类 m 方法的返回值是 Integer 是父类 m 方法返回值 Number 的子类</span></span><br><span class="line">	<span class="keyword">public</span> Integer <span class="title function_">m</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于子类，java 编译器会做如下处理：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> <span class="keyword">extends</span> <span class="title class_">A</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> Integer <span class="title function_">m</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 此方法才是真正重写了父类 public Number m() 方法</span></span><br><span class="line">	<span class="keyword">public</span> synthetic bridge Number <span class="title function_">m</span><span class="params">()</span> &#123;</span><br><span class="line">	<span class="comment">// 调用 public Integer m()</span></span><br><span class="line">		<span class="keyword">return</span> m();<span class="comment">//return时会向上转型，合乎语法</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中桥接方法比较特殊，仅对 java 虚拟机可见，并且与原来的 public Integer m() 没有命名冲突，可以用下面反射代码来验证：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (Method m : B.class.getDeclaredMethods()) &#123;</span><br><span class="line">	System.out.println(m);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//输出</span></span><br><span class="line"><span class="keyword">public</span> java.lang.Integer test.candy.B.m()</span><br><span class="line"><span class="keyword">public</span> java.lang.Number test.candy.B.m()<span class="comment">//合成方法</span></span><br></pre></td></tr></table></figure>

<h4 id="匿名内部类"><a href="#匿名内部类" class="headerlink" title="匿名内部类"></a>匿名内部类</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy11</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">         <span class="meta">@Override</span></span><br><span class="line">         <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;running...&quot;</span>);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>转换后的代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy11</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">      <span class="comment">//用额外创建的类来创建匿名内部类对象</span></span><br><span class="line">      <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Candy11$1</span>();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建了一个额外的类</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Candy11$1</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> Candy11$<span class="number">1</span>() &#123;&#125;</span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;running...&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果匿名内部类中引用了<strong>局部变量</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy11</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="keyword">final</span> <span class="type">int</span> x)</span> &#123;</span><br><span class="line">		<span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">			<span class="meta">@Override</span></span><br><span class="line">			<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">				System.out.println(<span class="string">&quot;ok:&quot;</span> + x);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>转换后代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 额外生成的类</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Candy11$1</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">	<span class="type">int</span> val$x;</span><br><span class="line">	Candy11$<span class="number">1</span>(<span class="type">int</span> x) &#123;</span><br><span class="line">		<span class="built_in">this</span>.val$x = x;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">		System.out.println(<span class="string">&quot;ok:&quot;</span> + <span class="built_in">this</span>.val$x);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>匿名内部类多了一个属性，需要把外部类的局部变量传递过来并储存起来。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Candy11</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="keyword">final</span> <span class="type">int</span> x)</span> &#123;</span><br><span class="line">		<span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Candy11$1</span>(x);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>注意</strong></p>
<p>这同时解释了为什么匿名内部类引用局部变量时，局部变量必须是 final 的：因为在创建 Candy11$1 对象时，将 x 的值赋值给了 Candy11$1 对象的 val$x 属性，所以 x 不应该再发生变化了，如果变化，那么 val$x 属性没有机会再跟着一起变化。</p>
</blockquote>
<h3 id="类加载阶段"><a href="#类加载阶段" class="headerlink" title="类加载阶段"></a>类加载阶段</h3><h4 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h4><ul>
<li>将类的字节码载入方法区，内部采用 C++ 的 instanceKlass 描述 java 类，它的重要 ﬁeld 有：<ul>
<li>_java_mirror 即 java 的类镜像，例如对 String 来说，它的镜像类就是 String.class，作用是把 klass 暴露给 java 使用，也就是通过镜像去访问instanceKlass </li>
<li>_super 即父类</li>
<li>_ﬁelds 即成员变量</li>
<li>_methods 即方法</li>
<li>_constants 即常量池</li>
<li>_class_loader 即类加载器</li>
<li>_vtable 虚方法表</li>
<li>_itable 接口方法</li>
</ul>
</li>
<li>如果这个类还有父类没有加载，<strong>先加载父类</strong></li>
<li>加载和链接可能是<strong>交替运行</strong>的</li>
</ul>
<blockquote>
<p>注意：</p>
<p>nstanceKlass 这样的【元数据】是存储在方法区（1.8 后的元空间内），但 _java_mirror 是存储在堆中，可以通过前面介绍的 HSDB 工具查看</p>
</blockquote>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200611205050.png" alt="img"></p>
<ul>
<li>InstanceKlass和*.class(JAVA镜像类)互相保存了对方的地址</li>
<li>类的对象在对象头中保存了*.class的地址。让对象可以通过其找到方法区中的instanceKlass，从而获取类的各种信息。</li>
</ul>
<h4 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h4><h5 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h5><p>验证类是否符合 JVM规范，安全性检查</p>
<p>用UE等支持二进制的编辑器修改 HelloWorld.class 的魔数，在控制台运行就会报错。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Error: A JNI error has occurred, please check your installation and try again</span><br><span class="line">Exception in thread &quot;main&quot; java.lang.ClassFormatError: Incompatible magic value</span><br></pre></td></tr></table></figure>

<h5 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h5><p>为 static 变量分配空间，设置默认值</p>
<ul>
<li>static变量在JDK 7以前是存储在方法区instanceKlass末尾。但在JDK 7以后就存储在_java_mirror末尾了，也就是堆空间。</li>
<li>static变量在分配空间和赋值是在两个阶段完成的。分配空间在准备阶段完成，赋值在初始化阶段完成</li>
<li>如果 static 变量是 ﬁnal 的<strong>基本类型</strong>，以及<strong>字符串常量</strong>，那么编译阶段值就确定了，<strong>赋值在准备阶段完成</strong></li>
<li>如果 static 变量是 ﬁnal 的，但属于<strong>引用类型</strong>，那么赋值也会在<strong>初始化阶段完成</strong></li>
</ul>
<h5 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h5><p>将常量池中的符号引用解析为直接引用，未解析时，常量池中的看到的对象仅是符号，未真正的存在于内存中。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo1</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, ClassNotFoundException &#123;</span><br><span class="line">      <span class="type">ClassLoader</span> <span class="variable">loader</span> <span class="operator">=</span> Demo1.class.getClassLoader();</span><br><span class="line">      <span class="comment">//loadClass只加载不解析</span></span><br><span class="line">      Class&lt;?&gt; c = loader.loadClass(<span class="string">&quot;com.nyima.JVM.day8.C&quot;</span>);</span><br><span class="line">      System.in.read();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span> &#123;</span><br><span class="line">   <span class="type">D</span> <span class="variable">d</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">D</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">D</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>打开HSDB，可以看到此时只加载了类C</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200611223153.png" alt="img"></p>
<p>查看类C的常量池，可以看到类D<strong>未被解析</strong>，只是存在于常量池中的符号</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200611230658.png" alt="img"></p>
<p>解析以后，会将常量池中的符号引用解析为直接引用</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/20200613104723.png" alt="img"></p>
<h4 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h4><p>初始化阶段就是执行类构造器clinit()方法的过程，虚拟机会保证这个类的『构造方法』的线程安全。</p>
<ul>
<li>clinit()方法是由编译器自动收集类中的所有类变量的<strong>赋值动作和静态代码块</strong>（static{}块）中的语句合并产生的。（编译器收集的顺序是由语句在源文件中<strong>出现的顺序决定</strong>的，静态语句块中只能访问到定义在静态语句块之前的变量）</li>
</ul>
<p>发生的时机：</p>
<p>概括得说，类初始化是【懒惰的】</p>
<ul>
<li>main 方法所在的类，总会被首先初始化</li>
<li>首次访问这个类的静态变量或静态方法时</li>
<li>子类初始化，如果父类还没初始化，会引发</li>
<li>Class.forName</li>
<li>new 会导致初始化</li>
</ul>
<p>以下情况不会初始化</p>
<ul>
<li>访问类的 static ﬁnal 静态常量（基本类型和字符串），（在类的链接的准备阶段就完成了）</li>
<li>类对象.class 不会触发初始化（类加载时完成）</li>
<li>创建该类对象的数组</li>
<li>类加载器的.loadClass方法</li>
<li>Class.forNamed的参数2为false时</li>
</ul>
<p><strong>验证类是否被初始化，可以看改类的静态代码块是否被执行</strong></p>
<p>典型应用—完成懒惰初始化单例模式</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">	<span class="keyword">private</span> <span class="title function_">Singleton</span><span class="params">()</span> &#123; &#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">LazyHolder</span> &#123;</span><br><span class="line">		<span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Singleton</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> LazyHolder.INSTANCE;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>静态内部类在外部类装载时并不会实例化，在调用时才会初始化，且只会初始化一次，线程安全</strong>。</p>
<h3 id="类加载器"><a href="#类加载器" class="headerlink" title="类加载器"></a>类加载器</h3><p>类加载器虽然只用于实现类的加载动作，但它在Java程序中起到的作用却远超类加载阶段</p>
<p>对于任意一个类，都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性，每一个类加载器，都拥有一个独立的类名称空间。这句话可以表达得更通俗一些：比较两个类是否“相等”，只有在这两个类是由同一个类加载器加载的前提下才有意义</p>
<p>以JDK8为例</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>加载的类</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>Bootstrap ClassLoader（启动类加载器）</td>
<td>JAVA_HOME/jre/lib</td>
<td>无法直接访问</td>
</tr>
<tr>
<td>Extension ClassLoader(拓展类加载器)</td>
<td>JAVA_HOME/jre/lib/ext</td>
<td>上级为Bootstrap，显示为null</td>
</tr>
<tr>
<td>Application ClassLoader(应用程序类加载器)</td>
<td>classpath</td>
<td>上级为Extension</td>
</tr>
<tr>
<td>自定义类加载器</td>
<td>自定义</td>
<td>上级为Application</td>
</tr>
</tbody></table>
<p>用 Bootstrap 类加载器加载类，可以替换一些核心类</p>
<ul>
<li><p>java -Xbootclasspath: <new bootclasspath></p>
</li>
<li><p>java -Xbootclasspath/a: &lt;追加路径&gt;</p>
</li>
<li><p>java -Xbootclasspath/p: &lt;追加路径&gt;</p>
</li>
</ul>
<h4 id="双亲委派模式"><a href="#双亲委派模式" class="headerlink" title="双亲委派模式"></a>双亲委派模式</h4><p>双亲委派模式，即调用类加载器ClassLoader 的 loadClass 方法时，查找类的规则</p>
<p>loadClass源码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; loadClass(String name, <span class="type">boolean</span> resolve)</span><br><span class="line">    <span class="keyword">throws</span> ClassNotFoundException</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (getClassLoadingLock(name)) &#123;</span><br><span class="line">        <span class="comment">// 首先查找该类是否已经被该类加载器加载过了</span></span><br><span class="line">        Class&lt;?&gt; c = findLoadedClass(name);</span><br><span class="line">        <span class="comment">//如果没有被加载过</span></span><br><span class="line">        <span class="keyword">if</span> (c == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">t0</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//看是否被它的上级加载器加载过了 Extension的上级是Bootstarp，但它显示为null</span></span><br><span class="line">                <span class="keyword">if</span> (parent != <span class="literal">null</span>) &#123;</span><br><span class="line">                    c = parent.loadClass(name, <span class="literal">false</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">//看是否被启动类加载器加载过</span></span><br><span class="line">                    c = findBootstrapClassOrNull(name);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">                <span class="comment">// ClassNotFoundException thrown if class not found</span></span><br><span class="line">                <span class="comment">// from the non-null parent class loader</span></span><br><span class="line">                <span class="comment">//捕获异常，但不做任何处理</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (c == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">//如果还是没有找到，先让拓展类加载器调用findClass方法去找到该类，如果还是没找到，就抛出异常</span></span><br><span class="line">                <span class="comment">//然后让应用类加载器去找classpath下找该类</span></span><br><span class="line">                <span class="type">long</span> <span class="variable">t1</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">                c = findClass(name);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 记录时间</span></span><br><span class="line">                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);</span><br><span class="line">                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);</span><br><span class="line">                sun.misc.PerfCounter.getFindClasses().increment();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (resolve) &#123;</span><br><span class="line">            resolveClass(c);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> c;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Load5_3</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">		Class&lt;?&gt; aClass = Load5_3.class.getClassLoader().loadClass(<span class="string">&quot;cn.itcast.jvm.t3.load.H&quot;</span>);</span><br><span class="line">		System.out.println(aClass.getClassLoader());</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>debug执行流程为：</p>
<ol>
<li>AppClassLoader ， 开始查看已加载的类，结果没有</li>
<li>AppClassLoader，委派上级</li>
<li>ExtClassLoader查看已加载的类，结果没有</li>
<li>ExtClassLoader没有上级了，则委派 BootstrapClassLoader 查找</li>
<li>BootstrapClassLoader 是在 JAVA_HOME/jre/lib 下找 H 这个类，显然没有（看不到源码）</li>
<li>ExtClassLoader，调用自己的 findClass 方法，是在JAVA_HOME/jre/lib/ext 下找 H 这个类，显然没有，抛出ClassNotFoundException</li>
<li>AppClassLoader，调用它自己的 findClass 方法，在 classpath 下查找，找到了</li>
</ol>
<h4 id="线程上下文类加载器"><a href="#线程上下文类加载器" class="headerlink" title="线程上下文类加载器"></a>线程上下文类加载器</h4><p>在使用 JDBC 时，都需要加载 Driver 驱动，不写也是可以让 com.mysql.jdbc.Driver 正确加载的</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class.forName(&quot;com.mysql.jdbc.Driver&quot;)</span><br></pre></td></tr></table></figure>

<p>怎么做到的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DriverManager</span> &#123;</span><br><span class="line">	<span class="comment">// 注册驱动的集合</span></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> CopyOnWriteArrayList&lt;DriverInfo&gt; registeredDrivers</span><br><span class="line">	= <span class="keyword">new</span> <span class="title class_">CopyOnWriteArrayList</span>&lt;&gt;();</span><br><span class="line">	<span class="comment">// 初始化驱动</span></span><br><span class="line">	<span class="keyword">static</span> &#123;</span><br><span class="line">		loadInitialDrivers();</span><br><span class="line">		println(<span class="string">&quot;JDBC DriverManager initialized&quot;</span>);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>DriverManager 的类加载器Bootstrap ClassLoader，会到 JAVA_HOME/jre/lib 下搜索类，但  JAVA_HOME/jre/lib 下显然没有 mysql-connector-java-5.1.47.jar 包，这样问题来了，在 DriverManager 的静态代码块中，怎么能正确加载 com.mysql.jdbc.Driver 呢？</p>
<p>继续看 loadInitialDrivers() 方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">loadInitialDrivers</span><span class="params">()</span> &#123;</span><br><span class="line">...</span><br><span class="line">	<span class="keyword">for</span> (String aDriver : driversList) &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			println(<span class="string">&quot;DriverManager.Initialize: loading &quot;</span> + aDriver);</span><br><span class="line">			<span class="comment">// 这里的 ClassLoader.getSystemClassLoader() 就是应用程序类加载器</span></span><br><span class="line">			Class.forName(aDriver, <span class="literal">true</span>,ClassLoader.getSystemClassLoader());</span><br><span class="line">		&#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">			println(<span class="string">&quot;DriverManager.Initialize: load failed: &quot;</span> + ex);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Service Provider Interface （SPI）:约定如下，在 jar 包的 META-INF/services 包下，以接口全限定名名为文件，文件内容是实现类名称</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220312152901018.png" alt="image-20220312152901018"></p>
<p>这样就可以使用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ServiceLoader&lt;接口类型&gt; allImpls = ServiceLoader.load(接口类型.class);</span><br><span class="line">Iterator&lt;接口类型&gt; iter = allImpls.iterator();</span><br><span class="line"><span class="keyword">while</span>(iter.hasNext()) &#123;</span><br><span class="line">	iter.next();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>来得到实现类，体现的是【面向接口编程+解耦】的思想，在下面一些框架中都运用了此思想：</p>
<ul>
<li>JDBC</li>
<li>Servlet 初始化器 </li>
<li>Spring 容器</li>
<li>Dubbo（对 SPI 进行了扩展）</li>
</ul>
<p>接着看 ServiceLoader.load 方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;S&gt; ServiceLoader&lt;S&gt; <span class="title function_">load</span><span class="params">(Class&lt;S&gt; service)</span> &#123;</span><br><span class="line">	<span class="comment">// 获取线程上下文类加载器</span></span><br><span class="line">	<span class="type">ClassLoader</span> <span class="variable">cl</span> <span class="operator">=</span> Thread.currentThread().getContextClassLoader();</span><br><span class="line">	<span class="keyword">return</span> ServiceLoader.load(service, cl);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>线程上下文类加载器是当前线程使用的类加载器，默认就是应用程序类加载器，它内部又是由 Class.forName 调用了线程上下文类加载器完成类加载</p>
<h4 id="自定义类加载器"><a href="#自定义类加载器" class="headerlink" title="自定义类加载器"></a>自定义类加载器</h4><p>问问自己，什么时候需要自定义类加载器</p>
<ul>
<li>想加载非 classpath 随意路径中的类文件</li>
<li>都是通过接口来使用实现，希望解耦时，常用在框架设计</li>
<li>这些类希望予以隔离，不同应用的同名类都可以加载，不冲突，常见于 tomcat 容器</li>
</ul>
<p>步骤：</p>
<ol>
<li>继承 ClassLoader 父类 </li>
<li>要遵从双亲委派机制，重写 findClass 方法 注意不是重写 loadClass 方法，否则不会走双亲委派机制</li>
<li>读取类文件的字节码</li>
<li> 调用父类的 defineClass 方法来加载类 </li>
<li>使用者调用该类加载器的 loadClass 方法</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClassLoader</span> <span class="keyword">extends</span> <span class="title class_">ClassLoader</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span> <span class="comment">// name 就是类名称</span></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> <span class="string">&quot;e:\\myclasspath\\&quot;</span> + name + <span class="string">&quot;.class&quot;</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">ByteArrayOutputStream</span> <span class="variable">os</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">            Files.copy(Paths.get(path), os);</span><br><span class="line">            <span class="comment">// 得到字节数组</span></span><br><span class="line">            <span class="type">byte</span>[] bytes = os.toByteArray();</span><br><span class="line">            <span class="comment">// byte[] -&gt; *.class</span></span><br><span class="line">            <span class="keyword">return</span> defineClass(name, bytes, <span class="number">0</span>, bytes.length);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ClassNotFoundException</span>(<span class="string">&quot;类文件未找到&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">MyClassLoader</span> <span class="variable">classLoader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClassLoader</span>();</span><br><span class="line">        Class&lt;?&gt; c1 = classLoader.loadClass(<span class="string">&quot;MapImpl1&quot;</span>);</span><br><span class="line">        Class&lt;?&gt; c2 = classLoader.loadClass(<span class="string">&quot;MapImpl1&quot;</span>);</span><br><span class="line">        System.out.println(c1 == c2);<span class="comment">//true</span></span><br><span class="line">        <span class="type">MyClassLoader</span> <span class="variable">classLoader2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClassLoader</span>();</span><br><span class="line">        Class&lt;?&gt; c3 = classLoader2.loadClass(<span class="string">&quot;MapImpl1&quot;</span>);</span><br><span class="line">        System.out.println(c1 == c3);<span class="comment">//false</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>类加载器对象不同，也会导致类不同</p>
<h3 id="运行期优化"><a href="#运行期优化" class="headerlink" title="运行期优化"></a>运行期优化</h3><h4 id="即时编译"><a href="#即时编译" class="headerlink" title="即时编译"></a>即时编译</h4><p>先来个例子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JIT1</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">200</span>; i++) &#123;</span><br><span class="line">			<span class="type">long</span> <span class="variable">start</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">			<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">1000</span>; j++) &#123;</span><br><span class="line">				<span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="type">long</span> <span class="variable">end</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">			System.out.printf(<span class="string">&quot;%d\t%d\n&quot;</span>,i,(end - start));</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>会发现循环慢慢变快了，原因是：</p>
<h5 id="分层编译"><a href="#分层编译" class="headerlink" title="分层编译"></a>分层编译</h5><p><strong>JVM 将执行状态分成了 5 个层次：</strong></p>
<ul>
<li>0层：解释执行，用解释器将字节码翻译为机器码</li>
<li>1层：使用 C1 <strong>即时编译器</strong>编译执行（不带 proﬁling）</li>
<li>2层：使用 C1 即时编译器编译执行（带基本的profiling）</li>
<li>3层：使用 C1 即时编译器编译执行（带完全的profiling）</li>
<li>4层：使用 C2 即时编译器编译执行</li>
</ul>
<blockquote>
<p>proﬁling 是指在运行过程中收集一些程序执行状态的数据，例如【方法的调用次数】，【循环的回边次数】等</p>
</blockquote>
<p><strong>即时编译器（JIT）与解释器的区别</strong></p>
<ul>
<li>解释器将字节码<strong>解释</strong>为机器码，下次即使遇到相同的字节码，仍会执行重复的解释</li>
<li>即时编译器将一些字节码<strong>编译</strong>为机器码，<strong>并存入 Code Cache</strong>，下次遇到相同的代码，直接执行，无需再编译</li>
<li>解释器是将字节码解释为针对所有平台都通用的机器码</li>
<li>即时编译器根据平台类型，生成平台特定的机器码</li>
</ul>
<p>对于大部分的不常用的代码，无需耗费时间将其编译成机器码，而是采取解释执行的方式运行；另一方面，对于仅占据小部分的热点代码，则可以将其编译成机器码，以达到理想的运行速度。 执行效率上简单比较一下 Interpreter &lt; C1 &lt; C2，总的目标是发现热点代码（hotspot名称的由来），并优化这些热点代码</p>
<h5 id="逃逸分析"><a href="#逃逸分析" class="headerlink" title="逃逸分析"></a>逃逸分析</h5><p>参考资料：<a target="_blank" rel="noopener" href="https://docs.oracle.com/en/java/javase/12/vm/java-hotspot-virtual-machine-performance-enhancements.html#GUID-D2E3DC58-D18B-4A6C-8167-4A1DFB4888">https://docs.oracle.com/en/java/javase/12/vm/java-hotspot-virtual-machine-performance-enhancements.html#GUID-D2E3DC58-D18B-4A6C-8167-4A1DFB4888</a></p>
<p>逃逸分析（Escape Analysis）简单来讲就是，Java Hotspot 虚拟机可以分析新创建对象的使用范围，并决定是否在 Java 堆上分配内存的一项技术，C2 即时编译器会采用该技术。比如上述代码的Object对象根本不会用到。</p>
<p>逃逸分析的 JVM 参数如下：</p>
<ul>
<li>开启逃逸分析：-XX:+DoEscapeAnalysis</li>
<li>关闭逃逸分析：-XX:-DoEscapeAnalysis</li>
<li>显示分析结果：-XX:+PrintEscapeAnalysis</li>
</ul>
<p>逃逸分析技术在 Java SE 6u23+ 开始支持，并默认设置为启用状态，可以不用额外加这个参数</p>
<p>当一个对象没有逃逸时，可以得到以下几个虚拟机的优化</p>
<p><strong>锁消除</strong>：线程同步锁是非常牺牲性能的，当编译器确定当前对象只有当前线程使用，那么就会移除该对象的同步锁</p>
<p><strong>标量替换</strong>：<strong>基础类型</strong>和<strong>对象的引用</strong>可以理解为<strong>标量</strong>，它们不能被进一步分解。而能被进一步分解的量就是聚合量，比如：对象。如果一个对象没有发生逃逸，那压根就不用创建它，只会在栈或者寄存器上创建它用到的成员标量，节省了内存空间，也提升了应用程序性能</p>
<p><strong>栈上分配</strong>：当对象没有发生逃逸时，该对象就可以通过标量替换分解成成员标量分配在栈内存中，和方法的生命周期一致，随着栈帧出栈时销毁，减少了 GC 压力，提高了应用程序性能</p>
<h5 id="方法内联"><a href="#方法内联" class="headerlink" title="方法内联"></a>方法内联</h5><p>比如</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">private static int square(final int i) &#123;</span><br><span class="line">	return i * i;</span><br><span class="line">&#125;</span><br><span class="line">System.out.println(square(9));</span><br></pre></td></tr></table></figure>

<p>如果发现 square 是热点方法，并且长度不太长时，会进行内联，所谓的内联就是把方法内代码拷贝、 粘贴到调用者的位置：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="number">9</span> * <span class="number">9</span>)</span><br></pre></td></tr></table></figure>

<h5 id="字段优化"><a href="#字段优化" class="headerlink" title="字段优化"></a>字段优化</h5><p>JMH 基准测试请参考：<a target="_blank" rel="noopener" href="http://openjdk.java.net/projects/code-tools/jmh/">http://openjdk.java.net/projects/code-tools/jmh/</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>[] elements = randomInts(<span class="number">1_000</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">doSum</span><span class="params">(<span class="type">int</span> x)</span> &#123;sum += x;&#125;</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test1</span><span class="params">()</span> &#123;</span><br><span class="line">	<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; elements.length; i++) &#123;</span><br><span class="line">		doSum(elements[i]);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>doSum 方法是否内联会影响 elements 成员变量读取的优化。如果 doSum 方法内联了，刚才的 test1 方法会被优化成下面的样子（伪代码）：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test1</span><span class="params">()</span> &#123;</span><br><span class="line">	<span class="comment">// elements.length 首次读取会缓存起来 -&gt; int[] local</span></span><br><span class="line">    <span class="type">int</span>[] local = <span class="built_in">this</span>.elements;</span><br><span class="line">	<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; elements.length; i++) &#123; <span class="comment">// 后续 999 次 求长度 &lt;- local</span></span><br><span class="line">		sum += elements[i]; <span class="comment">// 1000 次取下标 i 的元素 &lt;- local</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以节省 1999 次 查找成员变量读取操作</p>
<h4 id="反射优化"><a href="#反射优化" class="headerlink" title="反射优化"></a>反射优化</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Reflect1</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">foo</span><span class="params">()</span> &#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;foo...&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> NoSuchMethodException, InvocationTargetException, IllegalAccessException &#123;</span><br><span class="line">      <span class="type">Method</span> <span class="variable">foo</span> <span class="operator">=</span> Reflect1.class.getMethod(<span class="string">&quot;foo&quot;</span>);</span><br><span class="line">      <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i&lt;=<span class="number">16</span>; i++) &#123;</span><br><span class="line">         foo.invoke(<span class="literal">null</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>foo.invoke 前面 0 ~ 15 次调用使用的是 MethodAccesso 的实现类NativeMethodAccessorImpl </p>
<p>NativeMethodAccessorImpl源码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">NativeMethodAccessorImpl</span> <span class="keyword">extends</span> <span class="title class_">MethodAccessorImpl</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Method method;</span><br><span class="line">    <span class="keyword">private</span> DelegatingMethodAccessorImpl parent;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> numInvocations;</span><br><span class="line"></span><br><span class="line">    NativeMethodAccessorImpl(Method var1) &#123;</span><br><span class="line">        <span class="built_in">this</span>.method = var1;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//每次进行反射调用，会让numInvocation与ReflectionFactory.inflationThreshold的值（15）进行比较，并使使得numInvocation的值加一</span></span><br><span class="line">	<span class="comment">//如果numInvocation&gt;ReflectionFactory.inflationThreshold，则会调用本地方法invoke0方法</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object var1, Object[] var2)</span> <span class="keyword">throws</span> IllegalArgumentException, InvocationTargetException &#123;</span><br><span class="line">        <span class="keyword">if</span> (++<span class="built_in">this</span>.numInvocations &gt; ReflectionFactory.inflationThreshold() &amp;&amp; !ReflectUtil.isVMAnonymousClass(<span class="built_in">this</span>.method.getDeclaringClass())) &#123;</span><br><span class="line">            <span class="type">MethodAccessorImpl</span> <span class="variable">var3</span> <span class="operator">=</span> (MethodAccessorImpl)(<span class="keyword">new</span> <span class="title class_">MethodAccessorGenerator</span>()).generateMethod(<span class="built_in">this</span>.method.getDeclaringClass(), <span class="built_in">this</span>.method.getName(), <span class="built_in">this</span>.method.getParameterTypes(), <span class="built_in">this</span>.method.getReturnType(), <span class="built_in">this</span>.method.getExceptionTypes(), <span class="built_in">this</span>.method.getModifiers());</span><br><span class="line">            <span class="built_in">this</span>.parent.setDelegate(var3);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> invoke0(<span class="built_in">this</span>.method, var1, var2);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setParent</span><span class="params">(DelegatingMethodAccessorImpl var1)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.parent = var1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> Object <span class="title function_">invoke0</span><span class="params">(Method var0, Object var1, Object[] var2)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>if条件不满足，就会调用本地方法invoke0</li>
<li>随着numInvocation的增大，当它大于ReflectionFactory.inflationThreshold的值16时，就会本地方法访问器替换为一个运行时动态生成的访问器，来提高效率<ul>
<li>这时会从反射调用变为<strong>正常调用</strong>，即直接调用 Reflect1.foo()</li>
</ul>
</li>
</ul>
<p>当调用到第 16 次（从0开始算）时，会采用运行时生成的类代替掉最初的实现，可以通过 debug 得到 类名为 sun.reflect.GeneratedMethodAccessor1</p>
<p>可以使用阿里的 arthas 工具</p>
<p>输入【jad + 类名】来进行反编译</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GeneratedMethodAccessor1</span> <span class="keyword">extends</span> <span class="title class_">MethodAccessorImpl</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object object, Object[] arrobject)</span> <span class="keyword">throws</span></span><br><span class="line">InvocationTargetException &#123;</span><br><span class="line">		block4 : &#123;</span><br><span class="line">			<span class="keyword">if</span> (arrobject == <span class="literal">null</span> || arrobject.length == <span class="number">0</span>) <span class="keyword">break</span> block4;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">			&#125;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 可以看到，已经是直接调用了</span></span><br><span class="line">			Reflect1.foo();</span><br><span class="line">			<span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (Throwable throwable) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">InvocationTargetException</span>(throwable);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (ClassCastException | NullPointerException runtimeException) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(Object.<span class="built_in">super</span>.toString());</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="内存模型"><a href="#内存模型" class="headerlink" title="内存模型"></a>内存模型</h2><p>【java 内存模型】是 Java Memory Model（JMM）的意思，不是【java 内存结构】</p>
<p>简单的说，JMM 定义了一套在多线程读写共享数据时（成员变量、数组）时，对数据的可见性、有序 性、和原子性的规则和保障</p>
<p>和并发编程一起单开一个笔记</p>

      
    </div>
    <div class="article-footer">
      <blockquote class="mt-2x">
  <ul class="post-copyright list-unstyled">
    
    <li class="post-copyright-link hidden-xs">
      <strong>本文链接：</strong>
      <a href="https://skyung.gitee.io/Skyung/2022/02/27/JVM/" title="JVM学习记录合集" target="_blank" rel="external">https://skyung.gitee.io/Skyung/2022/02/27/JVM/</a>
    </li>
    
    <li class="post-copyright-license">
      <strong>版权声明： </strong> 本博客所有文章除特别声明外，均采用 <a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external">CC BY 4.0 CN协议</a> 许可协议。转载请注明出处！
    </li>
  </ul>
</blockquote>


<div class="panel panel-default panel-badger">
  <div class="panel-body">
    <figure class="media">
      <div class="media-left">
        <a href="https://github.com/cofess" target="_blank" class="img-burn thumb-sm visible-lg">
          <img src="/Skyung/images/avatar.jpg" class="img-rounded w-full" alt="">
        </a>
      </div>
      <div class="media-body">
        <h3 class="media-heading"><a href="https://github.com/cofess" target="_blank"><span class="text-dark">Skyung</span><small class="ml-1x">Web Developer</small></a></h3>
        <div>脚踏实地，仰望星空</div>
      </div>
    </figure>
  </div>
</div>


    </div>
  </article>
  
    
  <section id="comments">
  	
      <div id="uyan_frame"></div>
    
  </section>


  
</div>

  <nav class="bar bar-footer clearfix" data-stick-bottom>
  <div class="bar-inner">
  
  <ul class="pager pull-left">
    
    <li class="prev">
      <a href="/Skyung/2022/03/01/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E2%80%94%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F/" title="设计模式—行为型模式"><i class="icon icon-angle-left" aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
    </li>
    
    
    <li class="next">
      <a href="/Skyung/2021/09/05/%E5%90%84%E7%B1%BB%E5%9E%8B%E7%AE%97%E6%B3%95%E6%80%BB%E7%BB%93/" title="(no title)"><span>下一篇&nbsp;&nbsp;</span><i class="icon icon-angle-right" aria-hidden="true"></i></a>
    </li>
    
    
    <li class="toggle-toc">
      <a class="toggle-btn " data-toggle="collapse" href="#collapseToc" aria-expanded="false" title="文章目录" role="button">    <span>[&nbsp;</span><span>文章目录</span>
        <i class="text-collapsed icon icon-anchor"></i>
        <i class="text-in icon icon-close"></i>
        <span>]</span>
      </a>
    </li>
    
  </ul>
  
  
  <!-- Button trigger modal -->
  <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal" data-target="#donateModal"><span>赏</span></button>
  <!-- <div class="wave-icon wave-icon-danger btn-donate" data-toggle="modal" data-target="#donateModal">
    <div class="wave-circle"><span class="icon"><i class="icon icon-bill"></i></span></div>
  </div> -->
  
  
  <div class="bar-right">
    
    <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter" data-mobile-sites="weibo,qq,qzone"></div>
    
  </div>
  </div>
</nav>
  
<!-- Modal -->
<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
  <div class="modal-dialog" role="document">
    <div class="modal-content donate">
      <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
      <div class="modal-body">
        <div class="donate-box">
          <div class="donate-head">
            <p>感谢您的支持，我会继续努力的!</p>
          </div>
          <div class="tab-content">
            <div role="tabpanel" class="tab-pane fade active in" id="alipay">
              <div class="donate-payimg">
                <img src="/Skyung/images/donate/alipay.jpg" alt="扫码支持" title="扫一扫" />
              </div>
              <p class="text-muted mv">扫码打赏，你说多少就多少</p>
              <p class="text-grey">打开支付宝扫一扫，即可进行扫码打赏哦</p>
            </div>
            <div role="tabpanel" class="tab-pane fade" id="wechatpay">
              <div class="donate-payimg">
                <img src="/Skyung/images/donate/wechat.jpg" alt="扫码支持" title="扫一扫" />
              </div>
              <p class="text-muted mv">扫码打赏，你说多少就多少</p>
              <p class="text-grey">打开微信扫一扫，即可进行扫码打赏哦</p>
            </div>
          </div>
          <div class="donate-footer">
            <ul class="nav nav-tabs nav-justified" role="tablist">
              <li role="presentation" class="active">
                <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay" aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
              </li>
              <li role="presentation" class="">
                <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab" aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i> 微信支付</a>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>



</main>

  <footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
	
	
    <ul class="social-links">
    	
        <li><a href="/Skyung/null" target="_blank" title="Github" data-toggle=tooltip data-placement=top><i class="icon icon-github"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Weibo" data-toggle=tooltip data-placement=top><i class="icon icon-weibo"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Twitter" data-toggle=tooltip data-placement=top><i class="icon icon-twitter"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Behance" data-toggle=tooltip data-placement=top><i class="icon icon-behance"></i></a></li>
        
        <li><a href="/Skyung/atom.xml" target="_blank" title="Rss" data-toggle=tooltip data-placement=top><i class="icon icon-rss"></i></a></li>
        
    </ul>

    <div class="copyright">
    	
        <div class="publishby">
        	Theme by <a href="https://github.com/cofess" target="_blank"> cofess </a>base on <a href="https://github.com/cofess/hexo-theme-pure" target="_blank">pure</a>.
        </div>
    </div>
</footer>
  <script src="//cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
</script>

<script src="/Skyung/js/plugin.min.js"></script>


<script src="/Skyung/js/application.js"></script>


    <script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/Skyung/',
        CONTENT_URL: '/Skyung/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>

<script src="/Skyung/js/insight.js"></script>






   




   
    
    <script defer type="text/javascript" src="http://v2.uyan.cc/code/uyan.js?uid=[object Object]"></script>








</body>
</html>